Java 核心特性详解:从基础到进阶
从 OOP 到虚拟线程,深入理解 Java 核心技术栈与设计哲学
目录
Java 作为一门历经二十余年依然活跃的编程语言,其设计理念和技术特性值得每一位开发者深入理解。本文将从核心特性出发,详细解析 Java 的技术优势与实现原理。
一、面向对象编程(OOP)
1.1 四大基本特性
Java 是完全面向对象的语言,支持以下核心概念:
封装(Encapsulation)
将数据(属性)和操作数据的方法绑定在一起,隐藏内部实现细节。
public class BankAccount {
private double balance; // 私有属性,外部无法直接访问
// 通过公共方法控制访问
public void deposit(double amount) {
if (amount > 0) {
this.balance += amount;
}
}
public double getBalance() {
return this.balance;
}
}继承(Inheritance)
子类继承父类的属性和方法,实现代码复用和扩展。
// 父类
public class Animal {
protected String name;
public void eat() {
System.out.println(name + " is eating");
}
}
// 子类
public class Dog extends Animal {
public void bark() {
System.out.println(name + " is barking");
}
}多态(Polymorphism)
同一操作作用于不同对象产生不同行为,分为编译时多态(重载)和运行时多态(重写)。
// 运行时多态
Animal animal = new Dog(); // 父类引用指向子类对象
animal.eat(); // 调用的是 Dog 类的 eat 方法(如果被重写)
// 编译时多态(方法重载)
public class Calculator {
public int add(int a, int b) { return a + b; }
public double add(double a, double b) { return a + b; }
public int add(int a, int b, int c) { return a + b + c; }
}抽象(Abstraction)
提取共性,隐藏复杂性,通过抽象类和接口实现。
// 抽象类
public abstract class Shape {
protected String color;
// 抽象方法,子类必须实现
public abstract double calculateArea();
// 具体方法
public void setColor(String color) {
this.color = color;
}
}
// 接口
public interface Drawable {
void draw(); // 隐式 public abstract
default void printInfo() { // 默认方法
System.out.println("This is drawable");
}
}1.2 类与对象的关系
| 概念 | 说明 | 示例 |
|---|---|---|
| 类(Class) | 对象的模板,定义属性和行为 | class Car { } |
| 对象(Object) | 类的实例 | Car myCar = new Car(); |
| 构造方法 | 创建对象时初始化 | public Car() { } |
| this | 指向当前对象引用 | this.name = name; |
| super | 指向父类引用 | super.method(); |
二、跨平台性(Write Once, Run Anywhere)
2.1 实现原理
Java 的跨平台性依赖于 JVM(Java Virtual Machine):
Java 源代码 (.java)
↓ 编译
字节码 (.class)
↓ JVM 解释/编译执行
Windows / Linux / macOS2.2 JVM 架构
┌─────────────────────────────────────────┐
│ Java 程序 │
├─────────────────────────────────────────┤
│ 类加载器子系统 (Class Loader Subsystem) │
├─────────────────────────────────────────┤
│ 运行时数据区 (Runtime Data Areas) │
│ ├─ 堆 (Heap) │
│ ├─ 虚拟机栈 (VM Stack) │
│ ├─ 本地方法栈 (Native Method Stack) │
│ ├─ 程序计数器 (PC Register) │
│ └─ 方法区 (Method Area) │
├─────────────────────────────────────────┤
│ 执行引擎 (Execution Engine) │
│ ├─ 解释器 (Interpreter) │
│ └─ JIT 编译器 (Just-In-Time Compiler) │
└─────────────────────────────────────────┘2.3 字节码示例
public class Hello {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}编译后使用 javap -c Hello 查看字节码:
Code:
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #3 // String Hello, World!
5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return三、自动内存管理(垃圾回收)
3.1 内存区域划分
┌─────────────────────────────────────┐
│ 堆内存 (Heap) │
│ ┌──────────────┬────────────────┐ │
│ │ 年轻代 │ 老年代 │ │
│ │ (Young Gen) │ (Old Gen) │ │
│ │ ├─ Eden │ │ │
│ │ ├─ S0 │ │ │
│ │ └─ S1 │ │ │
│ └──────────────┴────────────────┘ │
├─────────────────────────────────────┤
│ 元空间 (Metaspace) │
│ (替代永久代 PermGen) │
└─────────────────────────────────────┘3.2 垃圾回收算法
| 算法 | 原理 | 优点 | 缺点 |
|---|---|---|---|
| 标记-清除 | 标记存活对象,清除未标记 | 简单 | 产生碎片 |
| 复制 | 将存活对象复制到另一块内存 | 无碎片 | 内存减半 |
| 标记-整理 | 标记后移动存活对象到一端 | 无碎片 | 移动成本高 |
| 分代收集 | 年轻代用复制,老年代用标记-整理 | 高效 | 复杂 |
3.3 垃圾收集器对比
// JVM 参数设置收集器
// G1 收集器(JDK 9+ 默认)
java -XX:+UseG1GC -jar app.jar
// ZGC(低延迟,JDK 11+)
java -XX:+UseZGC -jar app.jar
// Shenandoah(低延迟,JDK 12+)
java -XX:+UseShenandoahGC -jar app.jar| 收集器 | 算法 | 停顿时间 | 适用场景 |
|---|---|---|---|
| Serial | 复制/标记-整理 | 长 | 单核小内存 |
| Parallel | 复制/标记-整理 | 较长 | 吞吐量优先 |
| CMS | 标记-清除 | 短 | 低延迟(已弃用) |
| G1 | 标记-整理 + 复制 | 可预测 | 大堆平衡型 |
| ZGC | 染色指针 | <10ms | 超大堆低延迟 |
| Shenandoah | Brooks 指针 | <10ms | 低延迟 |
3.4 内存泄漏示例
// 1. 静态集合导致的内存泄漏
public class MemoryLeak {
private static final List<Object> list = new ArrayList<>();
public void add(Object obj) {
list.add(obj); // 永远不会被释放
}
}
// 2. 未关闭的资源
public void readFile(String path) throws IOException {
// 错误:未关闭流
BufferedReader reader = new BufferedReader(new FileReader(path));
return reader.readLine();
}
// 正确:使用 try-with-resources
public void readFileCorrect(String path) throws IOException {
try (BufferedReader reader = new BufferedReader(new FileReader(path))) {
return reader.readLine();
}
}四、多线程与并发
4.1 线程创建方式
// 方式 1:继承 Thread 类
class MyThread extends Thread {
@Override
public void run() {
System.out.println("Thread running");
}
}
// 方式 2:实现 Runnable 接口(推荐)
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("Runnable running");
}
}
// 方式 3:使用 Lambda(Java 8+)
Thread t = new Thread(() -> System.out.println("Lambda thread"));
// 方式 4:使用线程池(推荐)
ExecutorService executor = Executors.newFixedThreadPool(4);
executor.submit(() -> System.out.println("Pool thread"));4.2 线程状态
NEW(新建)
↓ start()
RUNNABLE(可运行)
↓ wait() / join() / LockSupport.park()
WAITING(无限等待)
↓ notify() / notifyAll() / LockSupport.unpark()
RUNNABLE
↓ synchronized / lock.lock()
BLOCKED(阻塞)
↓ 获取锁成功
RUNNABLE
↓ sleep(time) / wait(time) / join(time)
TIMED_WAITING(限时等待)
↓ 时间到 / notify()
RUNNABLE
↓ run() 结束
TERMINATED(终止)4.3 同步机制
public class SynchronizedExample {
private int count = 0;
private final Object lock = new Object();
// 方式 1:synchronized 方法
public synchronized void increment() {
count++;
}
// 方式 2:synchronized 代码块
public void incrementBlock() {
synchronized (lock) {
count++;
}
}
}
// Java 5+ 的 Lock 接口
public class LockExample {
private final ReentrantLock lock = new ReentrantLock();
private final Condition condition = lock.newCondition();
public void safeOperation() {
lock.lock();
try {
// 临界区代码
condition.await(); // 等待
condition.signal(); // 唤醒
} finally {
lock.unlock();
}
}
}4.4 并发工具类(JUC)
// 1. CountDownLatch - 等待多个线程完成
CountDownLatch latch = new CountDownLatch(3);
for (int i = 0; i < 3; i++) {
new Thread(() -> {
// 执行任务
latch.countDown();
}).start();
}
latch.await(); // 主线程等待
// 2. CyclicBarrier - 线程互相等待
CyclicBarrier barrier = new CyclicBarrier(3);
for (int i = 0; i < 3; i++) {
new Thread(() -> {
// 准备阶段
barrier.await(); // 到达屏障,等待其他线程
// 同时开始执行
}).start();
}
// 3. Semaphore - 控制并发数量
Semaphore semaphore = new Semaphore(10); // 最多10个并发
semaphore.acquire(); // 获取许可
// 执行操作
semaphore.release(); // 释放许可
// 4. CompletableFuture - 异步编程(Java 8+)
CompletableFuture.supplyAsync(() -> fetchData())
.thenApply(data -> process(data))
.thenAccept(result -> save(result))
.exceptionally(ex -> { handle(ex); return null; });4.5 volatile 与原子类
// volatile 保证可见性,但不保证原子性
private volatile boolean flag = false;
// 原子类保证原子操作
private AtomicInteger counter = new AtomicInteger(0);
public void increment() {
counter.incrementAndGet(); // 原子自增
}
// CAS 操作原理
public boolean compareAndSet(int expect, int update) {
// 如果当前值等于 expect,则更新为 update
}五、异常处理机制
5.1 异常体系
Throwable
├── Error(严重错误,不处理)
│ ├── OutOfMemoryError
│ ├── StackOverflowError
│ └── ...
└── Exception(可处理异常)
├── RuntimeException(运行时异常,非受检)
│ ├── NullPointerException
│ ├── ArrayIndexOutOfBoundsException
│ ├── IllegalArgumentException
│ └── ...
└── 其他 Exception(受检异常,必须处理)
├── IOException
├── SQLException
└── ...5.2 异常处理最佳实践
// 1. 精准捕获异常
try {
processFile();
} catch (FileNotFoundException e) {
// 文件不存在,提示用户
logger.error("File not found: {}", filePath);
} catch (IOException e) {
// IO 错误,可能需要重试
logger.error("IO error: {}", e.getMessage());
}
// 2. 使用 try-with-resources(Java 7+)
try (InputStream in = new FileInputStream(file);
OutputStream out = new FileOutputStream(dest)) {
// 自动关闭资源
copy(in, out);
} catch (IOException e) {
logger.error("Copy failed", e);
}
// 3. 自定义异常
public class BusinessException extends RuntimeException {
private final String errorCode;
public BusinessException(String errorCode, String message) {
super(message);
this.errorCode = errorCode;
}
// 不要丢失堆栈信息
public BusinessException(String message, Throwable cause) {
super(message, cause);
}
}
// 4. 异常转换
public void serviceMethod() {
try {
dao.operation();
} catch (SQLException e) {
// 转换为业务异常,隐藏底层细节
throw new BusinessException("DB_ERROR", "数据库操作失败", e);
}
}六、泛型(Generics)
6.1 基本用法
// 泛型类
public class Box<T> {
private T content;
public void setContent(T content) {
this.content = content;
}
public T getContent() {
return content;
}
}
// 使用
Box<String> stringBox = new Box<>();
stringBox.setContent("Hello");
String content = stringBox.getContent(); // 无需强制转换
// 泛型方法
public <T> void printArray(T[] array) {
for (T element : array) {
System.out.println(element);
}
}6.2 通配符
// ? extends T - 上界通配符(只读)
public void process(List<? extends Number> list) {
Number n = list.get(0); // 可以读取
// list.add(1); // 编译错误,不能写入
}
// ? super T - 下界通配符(只写)
public void addNumbers(List<? super Integer> list) {
list.add(1); // 可以写入
// Integer i = list.get(0); // 编译错误,读取只能是 Object
}
// PECS 原则
// Producer Extends, Consumer Super6.3 类型擦除
// 编译前
List<String> strList = new ArrayList<>();
List<Integer> intList = new ArrayList<>();
// 编译后(类型擦除)
List strList = new ArrayList();
List intList = new ArrayList();
// 运行时无法获取泛型类型
System.out.println(strList.getClass() == intList.getClass()); // true七、反射机制
7.1 获取 Class 对象
// 方式 1
Class<?> clazz1 = String.class;
// 方式 2
Class<?> clazz2 = "hello".getClass();
// 方式 3
Class<?> clazz3 = Class.forName("java.lang.String");7.2 反射操作
public class ReflectionDemo {
public static void main(String[] args) throws Exception {
Class<?> clazz = User.class;
// 创建实例
User user = (User) clazz.getDeclaredConstructor().newInstance();
// 获取方法
Method method = clazz.getDeclaredMethod("setName", String.class);
method.invoke(user, "Alice");
// 获取字段
Field field = clazz.getDeclaredField("age");
field.setAccessible(true); // 访问私有字段
field.setInt(user, 25);
// 获取注解
Annotation[] annotations = clazz.getAnnotations();
}
}7.3 反射的应用与注意
应用场景:
- 框架开发(Spring、MyBatis)
- 序列化/反序列化
- 动态代理
- 单元测试
注意事项:
- 性能开销较大(绕过编译优化)
- 破坏封装性
- 类型安全性降低
八、Java 8+ 新特性
8.1 Lambda 表达式
// 传统写法
Comparator<String> comparator = new Comparator<String>() {
@Override
public int compare(String s1, String s2) {
return s1.length() - s2.length();
}
};
// Lambda 写法
Comparator<String> lambda = (s1, s2) -> s1.length() - s2.length();
// 方法引用
Comparator<String> methodRef = Comparator.comparingInt(String::length);8.2 Stream API
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// 过滤、映射、收集
List<Integer> result = numbers.stream()
.filter(n -> n > 5) // 过滤大于5的
.map(n -> n * 2) // 乘以2
.sorted(Comparator.reverseOrder()) // 降序排序
.limit(3) // 取前3个
.collect(Collectors.toList());
// 聚合操作
int sum = numbers.stream().mapToInt(Integer::intValue).sum();
int max = numbers.stream().mapToInt(Integer::intValue).max().orElse(0);
// 分组
Map<String, List<Person>> groups = people.stream()
.collect(Collectors.groupingBy(Person::getCity));8.3 Optional
// 避免空指针异常
public String getCity(User user) {
return Optional.ofNullable(user)
.map(User::getAddress)
.map(Address::getCity)
.orElse("Unknown");
}
// 不要这样用(违背设计目的)
Optional<User> user = ...; // 作为字段或参数
if (user.isPresent()) { // 不要用 isPresent + get
...
}8.4 新日期时间 API
// 旧 API(线程不安全,设计差)
Date date = new Date();
Calendar calendar = Calendar.getInstance();
// 新 API(Java 8+)
LocalDate today = LocalDate.now();
LocalTime now = LocalTime.now();
LocalDateTime dateTime = LocalDateTime.now();
// 时区处理
ZonedDateTime zoned = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));
// 格式化
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String formatted = dateTime.format(formatter);
// 计算
LocalDate nextWeek = today.plusWeeks(1);
Period period = Period.between(startDate, endDate);
Duration duration = Duration.between(startTime, endTime);8.5 接口默认方法
public interface Animal {
void eat(); // 抽象方法
default void breathe() { // 默认方法
System.out.println("Breathing...");
}
static void info() { // 静态方法
System.out.println("This is an animal");
}
}九、模块系统(Java 9+)
9.1 模块化概述
myapp/
├── src/
│ └── com.myapp/
│ ├── module-info.java
│ └── com/myapp/
│ └── Main.java
└── lib/
└── com.modules.utilities.jar9.2 module-info.java
module com.myapp {
// 依赖的模块
requires java.base; // 默认隐式依赖
requires java.sql;
requires com.modules.utilities;
// 导出的包(对外可见)
exports com.myapp.api;
// 开放反射访问
opens com.myapp.internal;
// 服务提供/消费
provides com.myapp.spi.Service
with com.myapp.impl.ServiceImpl;
uses com.myapp.spi.Service;
}十、Java 21 新特性(LTS)
Java 21 是长期支持(LTS)版本,带来了革命性的并发编程模型改进,其中最引人注目的是**虚拟线程(Virtual Threads)**正式成为标准特性。
10.1 虚拟线程(Virtual Threads)
传统线程 vs 虚拟线程
| 特性 | 传统线程(Platform Thread) | 虚拟线程(Virtual Thread) |
|---|---|---|
| 实现方式 | 1:1 映射到 OS 线程 | JVM 调度,多对多映射 |
| 内存占用 | ~1-2 MB 栈空间 | ~几百字节 |
| 创建速度 | 慢(需系统调用) | 快(纯 JVM 操作) |
| 数量限制 | 数千级别 | 数百万级别 |
| 阻塞影响 | 阻塞会占用 OS 线程 | 阻塞时自动让出载体线程 |
| 适用场景 | 计算密集型 | I/O 密集型 |
虚拟线程原理
┌─────────────────────────────────────────────────────────┐
│ Java 应用层 │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ 虚拟线程 │ │ 虚拟线程 │ │ 虚拟线程 │ ... │
│ │ Virtual │ │ Virtual │ │ Virtual │ │
│ │ Thread │ │ Thread │ │ Thread │ │
│ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │
│ └─────────────────┼─────────────────┘ │
│ │ │
│ JVM 调度器(ForkJoinPool) │
│ │ │
│ ┌────────────────────────┼────────────────────────┐ │
│ │ 载体线程(Carrier Thread / Platform Thread) │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
│ │ │ Carrier 1│ │ Carrier 2│ │ Carrier N│ │ │
│ │ └──────────┘ └──────────┘ └──────────┘ │ │
│ └──────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
│
┌────────────┴────────────┐
▼ ▼
┌──────────┐ ┌──────────┐
│ OS 线程 1 │ │ OS 线程 N │
└──────────┘ └──────────┘虚拟线程使用方式
// 方式 1:使用 Thread.ofVirtual()(Java 21+)
Thread.startVirtualThread(() -> {
System.out.println("Running in virtual thread: " +
Thread.currentThread());
});
// 方式 2:使用 ExecutorService
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
IntStream.range(0, 10_000).forEach(i -> {
executor.submit(() -> {
Thread.sleep(Duration.ofSeconds(1)); // 阻塞操作
return i;
});
});
} // executor.close() 会等待所有任务完成
// 方式 3:ThreadFactory
ThreadFactory factory = Thread.ofVirtual().factory();
Thread vThread = factory.newThread(() -> {
// 任务代码
});
vThread.start();性能对比示例
public class ThreadComparison {
// 传统线程池处理 10000 个任务
public static void platformThreads() throws InterruptedException {
try (var executor = Executors.newFixedThreadPool(100)) {
long start = System.currentTimeMillis();
for (int i = 0; i < 10_000; i++) {
executor.submit(() -> {
Thread.sleep(100); // 模拟 I/O
return 0;
});
}
System.out.println("Platform threads: " +
(System.currentTimeMillis() - start) + "ms");
}
}
// 虚拟线程处理 10000 个任务
public static void virtualThreads() throws InterruptedException {
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
long start = System.currentTimeMillis();
for (int i = 0; i < 10_000; i++) {
executor.submit(() -> {
Thread.sleep(100); // 阻塞时自动让出
return 0;
});
}
System.out.println("Virtual threads: " +
(System.currentTimeMillis() - start) + "ms");
}
}
}典型输出:
Platform threads: 10000ms+ (线程池限制)
Virtual threads: 200ms (几乎同时调度)虚拟线程注意事项
// ⚠️ 不要这样做:在虚拟线程中使用 synchronized
virtualThread.execute(() -> {
synchronized (lock) { // 会 "钉住" 载体线程(Pinning)
// 长时间操作
}
});
// ✅ 推荐:使用 ReentrantLock
virtualThread.execute(() -> {
lock.lock(); // 不会钉住载体线程
try {
// 操作
} finally {
lock.unlock();
}
});
// ⚠️ 线程本地变量(ThreadLocal)在虚拟线程中开销大
// Java 21 引入了 ScopedValue 作为替代10.2 结构化并发(Structured Concurrency)
结构化并发让多线程编程像单线程一样清晰,子任务与父任务有明确的生命周期关系。
// Java 21 预览特性(需要 --enable-preview)
import java.util.concurrent.StructuredTaskScope;
public class StructuredConcurrencyDemo {
record User(String name, int orderCount) {}
record Order(String id, double amount) {}
// 传统写法:容易忘记关闭资源,异常处理复杂
public User fetchUserOldStyle(String userId) throws Exception {
ExecutorService executor = Executors.newFixedThreadPool(2);
Future<User> userFuture = executor.submit(() -> fetchUser(userId));
Future<Order> orderFuture = executor.submit(() -> fetchLatestOrder(userId));
User user = userFuture.get(); // 可能永远阻塞
Order order = orderFuture.get();
executor.shutdown(); // 容易遗漏
return new User(user.name(), order != null ? 1 : 0);
}
// 结构化并发写法
public User fetchUserStructured(String userId) throws Exception {
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
// 启动子任务
StructuredTaskScope.Subtask<User> userTask =
scope.fork(() -> fetchUser(userId));
StructuredTaskScope.Subtask<Order> orderTask =
scope.fork(() -> fetchLatestOrder(userId));
// 等待所有任务完成或失败
scope.join();
scope.throwIfFailed(); // 任一失败则抛出异常
// 获取结果
return new User(userTask.get().name(),
orderTask.get() != null ? 1 : 0);
} // 自动关闭,取消未完成任务
}
// 成功即返回模式(竞赛模式)
public String fetchFastest() throws Exception {
try (var scope = new StructuredTaskScope.ShutdownOnSuccess<String>()) {
scope.fork(() -> fetchFromServerA());
scope.fork(() -> fetchFromServerB());
scope.fork(() -> fetchFromServerC());
scope.join();
return scope.result(); // 返回最先成功的结果
}
}
}结构化并发优势:
- 清晰的错误传播:子任务失败,父任务自动取消其他子任务
- 确定的生命周期:
try-with-resources确保资源释放 - 可观测性:任务关系明确,便于调试
10.3 Record Patterns(记录模式)
Java 21 扩展了模式匹配,支持直接解构 Record 类。
// 定义记录
record Point(int x, int y) {}
record ColoredPoint(Point point, String color) {}
public class RecordPatternDemo {
// Java 17 写法
public void printPointOld(Object obj) {
if (obj instanceof Point p) {
int x = p.x();
int y = p.y();
System.out.println("x=" + x + ", y=" + y);
}
}
// Java 21 写法:直接解构
public void printPoint(Object obj) {
if (obj instanceof Point(int x, int y)) {
System.out.println("x=" + x + ", y=" + y);
// 无需通过 p.x() 访问
}
}
// 嵌套解构
public void printColoredPoint(Object obj) {
if (obj instanceof ColoredPoint(Point(int x, int y), String color)) {
System.out.println("Point at (" + x + "," + y + ") is " + color);
}
}
// 在 switch 中使用
public String describe(Object obj) {
return switch (obj) {
case Point(int x, int y) when x == 0 && y == 0 -> "原点";
case Point(int x, int y) when x == y -> "对角线上的点";
case Point(int x, _) when x < 0 -> "左侧的点";
case Point(_, int y) when y < 0 -> "下方的点";
case Point(int x, int y) -> "普通点 (" + x + "," + y + ")";
default -> "未知";
};
}
}10.4 Switch 模式匹配(标准特性)
Java 21 将 Switch 的模式匹配提升为标准特性。
public class SwitchPatternDemo {
// 类型模式 + null 处理 + 守卫条件
public String formatter(Object obj) {
return switch (obj) {
case null -> "null";
case Integer i when i > 0 -> "正整数: " + i;
case Integer i when i < 0 -> "负整数: " + i;
case Integer i -> "零";
case Long l -> "长整数: " + l;
case String s when s.isEmpty() -> "空字符串";
case String s when s.length() > 10 -> "长字符串: " + s.substring(0, 10) + "...";
case String s -> "字符串: " + s;
case List<?> list when list.isEmpty() -> "空列表";
case List<?> list -> "包含 " + list.size() + " 个元素的列表";
default -> "未知类型: " + obj.getClass().getName();
};
}
// 枚举穷尽匹配(无需 default)
enum Direction { NORTH, SOUTH, EAST, WEST }
public String directionName(Direction d) {
return switch (d) {
case NORTH -> "北";
case SOUTH -> "南";
case EAST -> "东";
case WEST -> "西";
// 无需 default,编译器会检查穷尽性
};
}
}10.5 顺序集合(Sequenced Collections)
Java 21 引入了统一的顺序集合接口。
// 新接口层次
// SequencedCollection extends Collection
// ├── SequencedSet extends SequencedCollection, Set
// └── SequencedMap extends Map
public void sequencedDemo() {
// List 本身就是有序的
List<String> list = new ArrayList<>();
list.addLast("B"); // 新增默认方法
list.addFirst("A");
String first = list.getFirst(); // "A"
String last = list.getLast(); // "B"
// LinkedHashSet 现在支持顺序操作
SequencedSet<String> set = new LinkedHashSet<>();
set.add("A");
set.add("B");
set.add("C");
SequencedSet<String> reversed = set.reversed(); // 反向视图
// reversed: C, B, A
// LinkedHashMap 支持顺序操作
SequencedMap<Integer, String> map = new LinkedHashMap<>();
map.putFirst(1, "A"); // 插入头部
map.putLast(2, "B"); // 插入尾部(等价于 put)
Map.Entry<Integer, String> firstEntry = map.firstEntry();
Map.Entry<Integer, String> lastEntry = map.lastEntry();
}10.6 String Templates(预览特性)
// 更安全的字符串拼接
public class StringTemplateDemo {
// 传统方式:容易 SQL 注入
public String buildQueryUnsafe(String userId) {
return "SELECT * FROM users WHERE id = '" + userId + "'";
// 如果 userId = "' OR '1'='1" 会导致注入
}
// Java 21 字符串模板(STR 处理器)
public String buildQuerySafe(String userId) {
// 自动转义,防止注入
return STR."SELECT * FROM users WHERE id = \"\{userId}\"";
}
// JSON 构造
public String buildJson(String name, int age) {
return STR."""
{
"name": "\{name}",
"age": \{age}
}
""";
}
// 自定义处理器
public void customProcessor() {
// FMT:格式化处理器
String formatted = FMT."Price: %10.2f\{price}";
// RAW:原始处理器(返回 StringTemplate 对象)
StringTemplate template = RAW."Hello \{name}";
}
}10.7 Java 21 与之前版本对比总结
| 特性 | Java 8 | Java 11 | Java 17 | Java 21 |
|---|---|---|---|---|
| Lambda | ✅ 新增 | ✅ | ✅ | ✅ |
| Stream | ✅ 新增 | ✅ | ✅ | ✅ |
| Optional | ✅ 新增 | ✅ | ✅ | ✅ |
| 模块系统 | ❌ | ❌ | ✅ 标准 | ✅ |
| var 推断 | ❌ | ✅ | ✅ | ✅ |
| 文本块 | ❌ | ❌ | ✅ | ✅ |
| Switch 表达式 | ❌ | ❌ | ✅ | ✅ |
| 密封类 | ❌ | ❌ | ✅ 标准 | ✅ |
| 模式匹配 instanceof | ❌ | ❌ | ✅ | ✅ |
| 虚拟线程 | ❌ | ❌ | ❌ | ✅ 标准 |
| 结构化并发 | ❌ | ❌ | ❌ | ✅ 预览 |
| Record 模式 | ❌ | ❌ | ❌ | ✅ 标准 |
| Switch 模式匹配 | ❌ | ❌ | 预览 | ✅ 标准 |
| 字符串模板 | ❌ | ❌ | ❌ | ✅ 预览 |
10.8 虚拟线程最佳实践
@Service
public class UserService {
private final HttpClient httpClient = HttpClient.newHttpClient();
// ✅ 适合虚拟线程:I/O 密集型
public List<User> fetchUsersAsync(List<String> userIds) {
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
return userIds.stream()
.map(id -> executor.submit(() -> fetchUser(id)))
.toList()
.stream()
.map(CompletableFuture::join)
.collect(Collectors.toList());
}
}
// ❌ 不适合:计算密集型(占用 CPU)
public BigInteger calculateFibonacci(int n) {
// 纯计算任务,虚拟线程无优势
// 应使用普通线程池
}
// ✅ 混合场景:I/O + 计算分离
public Result processData(List<String> urls) {
// I/O 部分用虚拟线程
List<Data> dataList;
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
dataList = urls.stream()
.map(url -> executor.submit(() -> download(url)))
.map(future -> {
try { return future.get(); }
catch (Exception e) { throw new RuntimeException(e); }
})
.collect(Collectors.toList());
}
// 计算部分用并行流(CPU 密集型)
return dataList.parallelStream()
.map(this::heavyComputation)
.collect(Collectors.collectingAndThen(
Collectors.toList(),
this::mergeResults
));
}
}十一、总结
| 特性类别 | 核心优势 | 学习重点 |
|---|---|---|
| 面向对象 | 封装、继承、多态、抽象 | 设计原则、设计模式 |
| 跨平台 | JVM 字节码 | JVM 内存结构、类加载机制 |
| 内存管理 | 自动垃圾回收 | GC 算法、调优参数 |
| 多线程 | 丰富的并发工具 | JUC 包、线程池、锁优化 |
| 异常处理 | 结构化异常机制 | 异常设计、最佳实践 |
| 泛型 | 类型安全、代码复用 | 通配符、类型擦除 |
| 反射 | 动态能力 | 框架原理、性能权衡 |
| 函数式 | 简洁、并行 | Stream、Lambda、Optional |
| 虚拟线程 (Java 21) | 轻量级、高并发 | 适用场景、结构化并发 |
| 模式匹配 (Java 21) | 简洁类型检查 | Record/Sealed Class 模式 |
参考资源:
- Oracle Java 官方文档
- Java 虚拟机规范
- Java 21 新特性官方文档
- Project Loom 官方文档
- 《Effective Java》
- 《深入理解 Java 虚拟机》
- 《Java 并发编程实战》