跳转至

Java新特性(8-21)

🎯 学习目标

完成本章学习后,你将能够: - 熟练使用Java 8 Lambda表达式与Stream API - 掌握Java 9-11的模块化、var推断等特性 - 理解Java 14-17的Records、Sealed Classes、Pattern Matching - 深入掌握Java 21虚拟线程(Virtual Threads)及结构化并发 - 能在新旧写法之间灵活切换,写出现代化的Java代码

Java 8-21特性演进时间线


1. Java 8 核心特性(最重要版本)

1.1 Lambda表达式

Lambda是匿名函数的简洁表示,使Java支持函数式编程风格。

Java
// ============ 旧写法:匿名内部类 ============
Runnable oldWay = new Runnable() {
    @Override
    public void run() {
        System.out.println("Hello");
    }
};

// ============ 新写法:Lambda ============
Runnable newWay = () -> System.out.println("Hello");  // Lambda表达式,简洁的匿名函数

// 排序对比
List<String> names = Arrays.asList("Charlie", "Alice", "Bob");

// 旧写法
Collections.sort(names, new Comparator<String>() {
    @Override
    public int compare(String a, String b) {
        return a.compareTo(b);
    }
});

// Lambda写法
names.sort((a, b) -> a.compareTo(b));

// 方法引用写法
names.sort(String::compareTo);

1.2 函数式接口

只有一个抽象方法的接口,可以用Lambda实例化。

Java
// JDK内置核心函数式接口
// Function<T, R>    — T → R(转换)
// Predicate<T>      — T → boolean(判断)
// Consumer<T>       — T → void(消费)
// Supplier<T>       — () → T(生产)
// UnaryOperator<T>  — T → T(一元操作)
// BiFunction<T,U,R> — (T, U) → R(双参数转换)

import java.util.function.*;

public class FunctionalInterfaceDemo {

    public static void main(String[] args) {
        // Function:转换
        Function<String, Integer> strLen = String::length;
        System.out.println(strLen.apply("Hello"));  // 5

        // 链式组合
        Function<String, String> trim = String::trim;
        Function<String, String> upper = String::toUpperCase;
        Function<String, String> pipeline = trim.andThen(upper);
        System.out.println(pipeline.apply("  hello  "));  // "HELLO"

        // Predicate:过滤
        Predicate<Integer> isPositive = n -> n > 0;
        Predicate<Integer> isEven = n -> n % 2 == 0;
        Predicate<Integer> isPositiveEven = isPositive.and(isEven);
        System.out.println(isPositiveEven.test(4));  // true

        // Consumer:消费
        Consumer<String> print = System.out::println;
        Consumer<String> printUpper = s -> System.out.println(s.toUpperCase());
        print.andThen(printUpper).accept("hello");
        // hello
        // HELLO

        // Supplier:延迟创建
        Supplier<List<String>> listFactory = ArrayList::new;
        List<String> list = listFactory.get();

        // 自定义函数式接口
        // @FunctionalInterface   // 编译期校验
        // public interface Transformer<T> {
        //     T transform(T input);
        // }
    }
}

1.3 Stream API(完整教程)

Java
import java.util.*;
import java.util.stream.*;

public class StreamDemo {

    // 注意:record是Java 16+特性,此处用于简化示例代码
    // Java 8环境请改用传统class定义(含构造器、getter等)
    record Employee(String name, String dept, double salary, int age) {}

    public static void main(String[] args) {
        List<Employee> employees = List.of(
            new Employee("Alice",   "Engineering", 120000, 30),
            new Employee("Bob",     "Engineering", 95000,  25),
            new Employee("Charlie", "Marketing",   80000,  28),
            new Employee("Diana",   "Engineering", 150000, 35),
            new Employee("Eve",     "Marketing",   70000,  23),
            new Employee("Frank",   "HR",          60000,  40)
        );

        // ===== 中间操作(惰性求值,返回Stream) =====

        // filter:过滤
        List<Employee> engineers = employees.stream()  // Stream API 声明式数据处理流水线
            .filter(e -> e.dept().equals("Engineering"))
            .toList();

        // map:映射/转换
        List<String> names = employees.stream()
            .map(Employee::name)
            .toList();

        // flatMap:一对多展平
        List<List<Integer>> nested = List.of(List.of(1, 2), List.of(3, 4));
        List<Integer> flat = nested.stream()
            .flatMap(Collection::stream)  // Collection::stream 等价于 list -> list.stream(),对每个子列表调用stream()后合并为一个流
            .toList();  // [1, 2, 3, 4]

        // sorted:排序
        List<Employee> sortedBySalary = employees.stream()
            .sorted(Comparator.comparingDouble(Employee::salary).reversed())
            .toList();

        // distinct:去重
        // peek:调试(中间操作)
        // limit / skip:截取 / 跳过

        // ===== 终端操作(触发流水线执行) =====

        // collect:收集为集合
        Map<String, List<Employee>> byDept = employees.stream()
            .collect(Collectors.groupingBy(Employee::dept));

        // 统计:各部门平均工资
        Map<String, Double> avgSalaryByDept = employees.stream()
            .collect(Collectors.groupingBy(
                Employee::dept,
                Collectors.averagingDouble(Employee::salary)
            ));

        // reduce:聚合
        double totalSalary = employees.stream()
            .mapToDouble(Employee::salary)
            .sum();

        Optional<Employee> highestPaid = employees.stream()
            .max(Comparator.comparingDouble(Employee::salary));

        // count / anyMatch / allMatch / noneMatch
        long count = employees.stream()
            .filter(e -> e.salary() > 100000)
            .count();

        boolean anyHighSalary = employees.stream()
            .anyMatch(e -> e.salary() > 140000);

        // forEach:遍历
        employees.stream()
            .filter(e -> e.age() < 30)
            .forEach(e -> System.out.println(e.name()));

        // toMap
        Map<String, Double> salaryMap = employees.stream()
            .collect(Collectors.toMap(Employee::name, Employee::salary));

        // joining
        String nameStr = employees.stream()
            .map(Employee::name)
            .collect(Collectors.joining(", "));

        // ===== 并行流 =====
        // 大数据量(> 10万)+ CPU密集型计算时考虑使用
        double total = employees.parallelStream()
            .mapToDouble(Employee::salary)
            .sum();

        System.out.println("总薪资: " + total);

        // ⚠️ parallelStream 安全使用要点:
        // ✅ 无状态操作(filter/map/reduce)是安全的
        // ❌ 不要在并行流中修改共享可变状态:
        //    List<String> results = new ArrayList<>();
        //    employees.parallelStream().forEach(e -> results.add(e.name())); // 竞态!
        //    正确写法:.collect(Collectors.toList())
        // ❌ 不要对小数据集使用(线程调度开销 > 计算收益)
        // ❌ 不要在并行流中执行IO操作(共享ForkJoinPool会被阻塞)
        // ❌ 不要对有序性敏感的操作使用(如 forEach 不保证顺序,用 forEachOrdered)
    }
}

1.4 Optional

Java
// ============ 旧写法:容易NPE ============
public String getCity(User user) {
    if (user != null) {
        Address addr = user.getAddress();
        if (addr != null) {
            return addr.getCity();
        }
    }
    return "Unknown";
}

// ============ 新写法:Optional链 ============
public String getCity(User user) {
    return Optional.ofNullable(user)  // Optional 安全处理可能为null的值
        .map(User::getAddress)
        .map(Address::getCity)
        .orElse("Unknown");
}

// Optional常用API
Optional<String> opt = Optional.of("Hello");
opt.isPresent();                    // true
opt.ifPresent(System.out::println); // 输出Hello
opt.orElse("default");              // "Hello"
opt.orElseGet(() -> "computed");    // "Hello"
opt.orElseThrow(() -> new RuntimeException("空值"));
opt.map(String::toUpperCase);       // Optional<"HELLO">
opt.filter(s -> s.length() > 3);    // Optional<"Hello">

// ⚠️ Optional 最佳实践:
// ✅ 用作方法返回值(表示"可能为空"的语义)
// ❌ 不要用作方法参数类型(增加调用复杂度,用@Nullable或重载代替)
// ❌ 不要用作字段类型(Optional不可序列化,增加内存开销)
// ❌ 不要用 Optional.get() 不检查(等于回到NPE)
// ❌ 不要用 Optional 包装集合(返回空集合 Collections.emptyList() 更好)

// Java 9+ 增强
opt.ifPresentOrElse(
    System.out::println,
    () -> System.out.println("空值")
);
opt.or(() -> Optional.of("backup")); // Optional链
opt.stream();                         // 转为Stream

1.5 新日期时间API(java.time)

Java
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;

// 旧: Date/Calendar(线程不安全、API混乱)
// 新: java.time包(不可变、线程安全、清晰API)

LocalDate date = LocalDate.now();                     // 2026-02-07
LocalDate birthday = LocalDate.of(1998, 5, 15);
LocalTime time = LocalTime.now();                     // 14:30:45.123
LocalDateTime dateTime = LocalDateTime.now();         // 日期+时间
ZonedDateTime zonedDT = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));
Instant instant = Instant.now();                      // UTC时间戳

// 操作
LocalDate nextWeek = date.plusWeeks(1);
LocalDate lastMonth = date.minusMonths(1);
long daysBetween = ChronoUnit.DAYS.between(birthday, date);

// 格式化
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String formatted = dateTime.format(fmt);
LocalDateTime parsed = LocalDateTime.parse("2026-02-07 14:30:00", fmt);

// Duration(时间段)和 Period(日期段)
Duration duration = Duration.between(LocalTime.of(9, 0), LocalTime.of(17, 30));
System.out.println(duration.toHours());  // 8
Period period = Period.between(birthday, date);
System.out.println(period.getYears() + "年");

2. Java 9-11

2.1 模块系统(JPMS,Java 9)

Java
// module-info.java — 模块描述符
module com.example.myapp {
    requires java.sql;              // 依赖其他模块
    requires transitive java.logging; // 传递依赖
    exports com.example.myapp.api;  // 导出包(对外可见)
    opens com.example.myapp.model to com.fasterxml.jackson.databind; // 对反射开放
}

2.2 局部变量类型推断 var(Java 10)

Java
// 旧写法
Map<String, List<Employee>> groupedMap = employees.stream()
    .collect(Collectors.groupingBy(Employee::dept));

// 新写法:var推断
var groupedMap = employees.stream()  // var 局部变量类型推断(Java 10+)
    .collect(Collectors.groupingBy(Employee::dept));

var list = new ArrayList<String>();   // 推断为 ArrayList<String>
var stream = list.stream();           // 推断为 Stream<String>

// 注意:var只能用于局部变量,不能用于方法参数、字段、返回值
// var不是关键字,仍可用作变量名(但不推荐)

2.3 HTTP Client(Java 11)

Java
// 旧: HttpURLConnection(繁琐)
// 新: java.net.http.HttpClient(支持HTTP/2, 异步, WebSocket)

import java.net.http.*;
import java.net.URI;

// 同步请求
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
    .uri(URI.create("https://api.example.com/users"))
    .header("Content-Type", "application/json")
    .GET()
    .build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.statusCode());
System.out.println(response.body());

// 异步请求
client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
    .thenApply(HttpResponse::body)
    .thenAccept(System.out::println)
    .join();

// POST请求
HttpRequest postReq = HttpRequest.newBuilder()
    .uri(URI.create("https://api.example.com/users"))
    .header("Content-Type", "application/json")
    .POST(HttpRequest.BodyPublishers.ofString("{\"name\":\"Alice\",\"age\":25}"))
    .build();

2.4 String增强(Java 11)

Java
// Java 11
"  hello  ".strip();          // "hello"(比trim更完善,支持Unicode空白)
"  hello  ".stripLeading();   // "hello  "
"  hello  ".stripTrailing();  // "  hello"
"".isBlank();                 // true(空字符串或纯空白)
"hello\nworld".lines().toList(); // ["hello", "world"]
"ha".repeat(3);               // "hahaha"

3. Java 14-17 LTS

3.1 Records(Java 16正式)

Java
// ============ 旧写法:POJO太冗长 ============
public class Point {
    private final int x;
    private final int y;

    public Point(int x, int y) { this.x = x; this.y = y; }
    public int getX() { return x; }
    public int getY() { return y; }

    @Override
    public boolean equals(Object o) { /* ... */ }
    @Override
    public int hashCode() { return Objects.hash(x, y); }
    @Override
    public String toString() { return "Point[x=" + x + ", y=" + y + "]"; }
}

// ============ 新写法:Record(一行搞定) ============
public record Point(int x, int y) {}  // record 不可变数据类,自动生成构造器/getter/equals/hashCode/toString
// 自动生成:构造器、getter(x()、y()而非getX())、equals、hashCode、toString
// Record是不可变的(final字段),不能继承其他类(隐式继承java.lang.Record)

// 可自定义构造器和方法
public record User(String name, String email, int age) {
    // 紧凑构造器(Compact Constructor)— 参数校验
    public User {
        if (age < 0) throw new IllegalArgumentException("年龄不能为负");
        name = name.strip();   // 可修改参数
        email = email.toLowerCase();
    }

    // 自定义方法
    public String displayName() {
        return name + " <" + email + ">";
    }
}

// 使用
var user = new User("Alice", "ALICE@example.com", 25);
System.out.println(user.name());        // "Alice"
System.out.println(user.displayName()); // "Alice <alice@example.com>"

3.2 Sealed Classes(Java 17正式)

Java
// 限制类的继承层级 — 只有permits指定的类可以继承
public sealed interface Shape permits Circle, Rectangle, Triangle {
    double area();
}

public record Circle(double radius) implements Shape {
    public double area() { return Math.PI * radius * radius; }
}

public record Rectangle(double width, double height) implements Shape {
    public double area() { return width * height; }
}

// final:不可再被继承
public final class Triangle implements Shape {
    private final double base, height;
    public Triangle(double base, double height) {
        this.base = base; this.height = height;
    }
    public double area() { return 0.5 * base * height; }
}

// 好处:编译器可以穷举检查(配合Pattern Matching switch极佳)

3.3 Pattern Matching(模式匹配)

Java
// ============ instanceof模式匹配 (Java 16+) ============

// 旧写法
if (obj instanceof String) {
    String s = (String) obj;
    System.out.println(s.length());
}

// 新写法:直接绑定变量
if (obj instanceof String s) {
    System.out.println(s.length());
}
// 还支持在条件中使用
if (obj instanceof String s && s.length() > 5) {
    System.out.println("长字符串: " + s);
}

// ============ switch模式匹配 (Java 21正式) ============
// 配合sealed classes实现穷举
public static String describe(Shape shape) {
    return switch (shape) {
        case Circle c    -> "圆形,半径=" + c.radius();
        case Rectangle r -> "矩形,面积=" + r.area();
        case Triangle t  -> "三角形,面积=" + t.area();
        // 无需default,因为Shape是sealed的,编译器知道所有子类
    };
}

// Guarded Pattern(带条件)
public static String classify(Shape shape) {
    return switch (shape) {
        case Circle c when c.radius() > 10  -> "大圆";
        case Circle c                       -> "小圆";
        case Rectangle r when r.area() > 100 -> "大矩形";
        case Rectangle r                     -> "小矩形";
        case Triangle t                      -> "三角形";
    };
}

3.4 Switch表达式(Java 14正式)

Java
// 旧写法:传统switch
String result;
switch (day) {
    case MONDAY:
    case FRIDAY:
        result = "工作日";
        break;
    case SATURDAY:
    case SUNDAY:
        result = "周末";
        break;
    default:
        result = "其他";
}

// 新写法:switch表达式(箭头语法 + 直接返回值)
String result = switch (day) {  // switch表达式,箭头语法直接返回值(Java 14+)
    case MONDAY, FRIDAY  -> "工作日";
    case SATURDAY, SUNDAY -> "周末";
    default -> "其他";
};

// 多行用yield返回
int value = switch (status) {
    case "active" -> 1;
    case "inactive" -> {
        log("处理inactive");
        yield 0;
    }
    default -> -1;
};

3.5 Text Blocks(Java 15正式)

Java
// 旧写法:字符串拼接地狱
String json = "{\n" +
    "  \"name\": \"Alice\",\n" +
    "  \"age\": 25\n" +
    "}";

// 新写法:文本块(三引号)
String json = """
        {
          "name": "Alice",
          "age": 25
        }
        """;

String sql = """
        SELECT u.name, u.email, o.amount
        FROM users u
        JOIN orders o ON u.id = o.user_id
        WHERE u.status = 'active'
        ORDER BY o.created_at DESC
        """;

String html = """
        <html>
            <body>
                <h1>Hello, %s!</h1>
            </body>
        </html>
        """.formatted("Alice");  // 支持formatted()

4. Java 19-21 LTS(重要突破版本)

4.1 虚拟线程 Virtual Threads(Java 21正式)

这是Java 21最重要的特性,彻底改变了Java并发编程模型。

Java
// ============ 传统平台线程(OS线程,重量级) ============
// 每个线程约占1MB栈内存,创建/切换开销大
// 典型Web服务器能支撑的并发线程数:数千

// ============ 虚拟线程(用户态线程,超轻量) ============
// 由JVM管理,不直接对应OS线程
// 每个虚拟线程仅占几KB,可轻松创建百万个
// 特别适合IO密集型任务(Web请求、数据库查询、RPC调用)

import java.util.concurrent.*;

public class VirtualThreadDemo {

    public static void main(String[] args) throws Exception {

        // 方式1:直接创建
        Thread vt = Thread.ofVirtual().name("my-vt").start(() -> {
            System.out.println("虚拟线程: " + Thread.currentThread());
        });
        vt.join();

        // 方式2:工厂创建
        ThreadFactory factory = Thread.ofVirtual().name("worker-", 0).factory();
        Thread t = factory.newThread(() -> System.out.println("Hello from VT"));
        t.start();

        // 方式3:ExecutorService(推荐,生产环境)
        try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
            // 提交100万个任务 — 平台线程不可能做到!
            List<Future<String>> futures = new ArrayList<>();
            for (int i = 0; i < 1_000_000; i++) {
                final int taskId = i;
                futures.add(executor.submit(() -> {
                    Thread.sleep(Duration.ofSeconds(1));  // 模拟IO
                    return "Task-" + taskId + " done";
                }));
            }
            // 等待所有完成
            int count = 0;
            for (var future : futures) {
                future.get();
                count++;
            }
            System.out.println("完成任务数: " + count);
        }
    }
}

// 对比:处理10000个HTTP请求
// 平台线程:需要线程池(200个线程),请求排队等待
// 虚拟线程:每个请求一个虚拟线程,IO阻塞时自动让出载体线程

// Spring Boot 3.2+ 启用虚拟线程
// application.yml:
// spring:
//   threads:
//     virtual:
//       enabled: true

4.2 结构化并发 Structured Concurrency(Java 21预览)

Java
// 将并发任务视为一个整体(类似try-with-resources管理线程生命周期)
import java.util.concurrent.StructuredTaskScope;

public class StructuredConcurrencyDemo {

    record UserProfile(String user, String orders, String recommendations) {}

    // 并行获取用户数据:用户信息 + 订单 + 推荐
    static UserProfile fetchUserProfile(long userId) throws Exception {
        try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
            // 同时发起三个子任务
            var userTask = scope.fork(() -> fetchUser(userId));
            var ordersTask = scope.fork(() -> fetchOrders(userId));
            var recsTask = scope.fork(() -> fetchRecommendations(userId));

            scope.join();            // 等待所有子任务完成
            scope.throwIfFailed();   // 任何一个失败则抛异常

            return new UserProfile(
                userTask.get(), ordersTask.get(), recsTask.get()
            );
        }
        // scope关闭时,未完成的子任务会被自动取消
    }

    static String fetchUser(long id) throws InterruptedException {
        Thread.sleep(100); return "User-" + id;
    }
    static String fetchOrders(long id) throws InterruptedException {
        Thread.sleep(200); return "Orders for " + id;
    }
    static String fetchRecommendations(long id) throws InterruptedException {
        Thread.sleep(150); return "Recs for " + id;
    }
}

4.3 Record Patterns(Java 21正式)

Java
// 解构Record组件
record Point(int x, int y) {}
record Line(Point start, Point end) {}

// 嵌套解构
static String describeLine(Object obj) {
    return switch (obj) {
        // 嵌套解构: 匹配Line记录并同时解构其内部两个Point,提取所有坐标分量; .formatted()等价于String.format()
        case Line(Point(var x1, var y1), Point(var x2, var y2)) ->
            "线段: (%d,%d) → (%d,%d)".formatted(x1, y1, x2, y2);
        case Point(var x, var y) ->
            "点: (%d,%d)".formatted(x, y);
        default -> "未知";
    };
}

// if语句中使用
if (obj instanceof Line(Point(var x1, var y1), Point(var x2, var y2))) {
    double length = Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));
    System.out.println("线段长度: " + length);
}

4.4 Sequenced Collections(Java 21正式)

Java
// 旧:链表取首尾元素方式不统一
// List: list.get(0) / list.get(list.size()-1)
// Deque: deque.getFirst() / deque.getLast()
// SortedSet: set.first() / set.last()

// 新:统一的SequencedCollection接口
// interface SequencedCollection<E> extends Collection<E> {
//     E getFirst();
//     E getLast();
//     void addFirst(E);
//     void addLast(E);
//     E removeFirst();
//     E removeLast();
//     SequencedCollection<E> reversed();
// }

var list = new ArrayList<>(List.of("A", "B", "C"));
System.out.println(list.getFirst());   // "A"
System.out.println(list.getLast());    // "C"
list.addFirst("Z");                    // ["Z", "A", "B", "C"]
list.reversed().forEach(System.out::println);  // C, B, A, Z

var map = new LinkedHashMap<String, Integer>();
map.put("one", 1);
map.put("two", 2);
map.put("three", 3);
System.out.println(map.firstEntry());  // one=1
System.out.println(map.lastEntry());   // three=3

5. 版本特性速查表

版本 LTS 关键特性
8 Lambda, Stream, Optional, java.time, 函数式接口, 默认方法
9 模块系统(JPMS), JShell, 集合工厂方法(List.of), 接口private方法
10 var局部变量推断, G1并行Full GC
11 HTTP Client, String新方法, 单文件运行(java Xxx.java), ZGC实验
12 Switch表达式(预览), String新方法(indent, transform)
13 Text Blocks(预览)
14 Records(预览), Pattern Matching instanceof(预览), 有用的NullPointerException
15 Sealed Classes(预览), Text Blocks(正式), ZGC/Shenandoah正式
16 Records(正式), Pattern Matching instanceof(正式), Vector API(孵化)
17 Sealed Classes(正式), switch模式匹配(预览), 移除AOT/Applet
18 UTF-8为默认字符集, 简单Web服务器
19 虚拟线程(预览), 结构化并发(孵化), Record Patterns(预览)
20 Scoped Values(孵化)
21 虚拟线程(正式), Record Patterns(正式), switch模式匹配(正式), Sequenced Collections, 分代ZGC

✏️ 练习

  1. Java 8:使用Stream API对一组学生数据进行分组统计(按班级分组计算平均分,筛选出TOP3高分学生,拼接所有学生姓名为逗号分隔字符串)
  2. Java 17:定义sealed interface Expression(子类:NumberAddMultiply),使用switch模式匹配实现表达式求值器
  3. Java 21:使用Virtual Threads + Structured Concurrency并行请求3个模拟API,汇总结果;对比平台线程和虚拟线程分别创建10000个线程的内存占用和耗时

📋 面试要点

  1. Stream vs for循环? — Stream声明式、链式、支持并行,适合集合变换;for命令式,性能更可控,适合简单遍历和需要break/continue的场景
  2. Lambda捕获变量为什么要effectively final? — Lambda可能延迟执行或在另一个线程执行,可变变量会导致数据竞争和不确定行为
  3. Record和普通类的区别? — Record不可变(final字段)、自动生成equals/hashCode/toString、不能继承其他类(隐式继承Record)、可实现接口
  4. 虚拟线程vs平台线程? — 虚拟线程由JVM调度、轻量(KB vs MB)、适合IO密集型、可创建百万级;平台线程对应OS线程、适合CPU密集型
  5. Sealed Classes的作用? — 限制继承层级,使类型系统更精确;配合模式匹配可编译期穷举检查,防止遗漏分支
  6. var是动态类型吗? — 不是!Java仍是强类型,var只是编译期类型推断(语法糖),编译后有明确类型

📌 下一步学习15-Java并发编程进阶 — 深入理解JMM和并发工具