效率翻倍新技能:JDK8后的新特性

以下是 JDK 8 至 JDK 21 中最具实用性的新特性整理,涵盖语言特性、工具类增强、性能优化等方向,附代码示例和注释说明:

一、JDK 8(2014):函数式编程与现代化API

JDK 8 是 Java 发展的里程碑版本,引入了大量颠覆性特性,至今仍是企业级项目的基础。

  1. Lambda 表达式

简化匿名内部类,支持函数式编程,使代码更简洁。

示例:用 Lambda 实现 Runnable 和 Comparator

// 传统匿名内部类

new Thread(new Runnable() {

@Override

public void run() {

System.out.println("传统方式");

}

}).start();

// Lambda 表达式(简化版)

new Thread(() -> System.out.println("Lambda 方式")).start();

// 传统 Comparator 排序

List list = Arrays.asList("banana", "apple", "cherry");
Collections.sort(list, new Comparator () {
@Override
public int compare(String a, String b) {
return a.compareTo(b);
}
});

// Lambda 排序(更简洁)

Collections.sort(list, (a, b) -> a.compareTo(b));

// 或直接使用方法引用:list.sort(String::compareTo);

  1. Stream API

提供声明式集合操作(过滤、映射、聚合等),支持并行计算。

示例:用 Stream 处理集合

List numbers = Arrays.asList(1, 2, 3, 4, 5, 6);

// 过滤偶数,平方后求和

int sum = numbers.stream()

.filter(n -> n % 2 == 0) // 过滤偶数

.mapToInt(n -> n * n) // 平方

.sum(); // 求和

System.out.println(sum); // 输出:2² + 4² + 6² = 4 + 16 + 36 = 56

  1. Optional 类

解决空指针异常(NPE),显式处理可能为空的值。

示例:避免空指针

public class OptionalDemo {

public static String getUsername(User user) {

// 如果 user/user.getName() 为 null,返回 "未知用户"

return Optional.ofNullable(user) // 包装可能为空的对象

.map(User::getName) // 提取 name(若 user 非空)

.orElse("未知用户"); // 最终默认值

}

复制代码
public static void main(String[] args) {
    User user = new User("张三");
    User nullUser = null;

    System.out.println(getUsername(user));   // 输出:张三
    System.out.println(getUsername(nullUser));// 输出:未知用户
}

static class User {
    private String name;
    public User(String name) { this.name = name; }
    public String getName() { return name; }
}

}

  1. 新的日期时间 API(java.time)

替代老旧的 Date 和 SimpleDateFormat,线程安全且设计更合理。

示例:日期时间操作

// 获取当前日期(不含时间)

LocalDate today = LocalDate.now();

System.out.println("今天:" + today); // 输出:2025-09-17

// 解析日期字符串

LocalDate birthday = LocalDate.parse("1990-05-20");

System.out.println("生日:" + birthday); // 输出:1990-05-20

// 计算两个日期的间隔(天数)

long daysBetween = ChronoUnit.DAYS.between(birthday, today);

System.out.println("距离生日已过:" + daysBetween + "天");

// 时间操作(带时区)

ZonedDateTime zonedDateTime = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));

System.out.println("上海时间:" + zonedDateTime); // 输出:2025-09-17T16:45:00+08:00[Asia/Shanghai]

二、JDK 9(2017):模块化与基础增强

JDK 9 引入了模块化系统(JPMS),并对核心类库做了多项实用改进。

  1. 模块化系统(JPMS)

通过 module-info.java 明确模块依赖,提升代码封装性和安全性。

示例:模块定义与依赖

// 模块描述文件:module-info.java

module com.example.app {

requires java.base; // 依赖基础模块(所有模块隐式依赖)

requires com.example.utils; // 显式依赖自定义工具模块

exports com.example.app.api; // 导出包供其他模块使用

}

// 模块内代码(com/example/app/api/Calculator.java)

package com.example.app.api;

public class Calculator {

public int add(int a, int b) { return a + b; }

}

  1. 不可变集合工厂方法

List、Set、Map 新增静态工厂方法(如 of()、ofEntries()),快速创建不可变集合。

示例:创建不可变集合

// 不可变 List(线程安全,无法添加/删除元素)

List immutableList = List.of("a", "b", "c");
// immutableList.add("d"); // 抛出 UnsupportedOperationException

// 不可变 Set(无重复元素)

Set immutableSet = Set.of(1, 2, 3);

// 不可变 Map(键值对固定)

Map<String, Integer> immutableMap = Map.of("one", 1, "two", 2);

// 若需多个键值对,使用 Map.ofEntries()

Map<String, Integer> map = Map.ofEntries(

entry("three", 3),

entry("four", 4)

);

  1. Stream 增强

新增 takeWhile()、dropWhile()(按条件截断流)和 ofNullable()(处理空值)。

示例:Stream 截断与空值处理

List numbers = Arrays.asList(2, 4, 6, 7, 8, 10);

// takeWhile:遇到第一个不满足条件的元素后停止处理(顺序流)

numbers.stream()

.takeWhile(n -> n % 2 == 0) // 取前三个偶数(2,4,6)

.forEach(System.out::print); // 输出:246

// dropWhile:跳过前几个满足条件的元素,直到遇到不满足的(顺序流)

numbers.stream()

.dropWhile(n -> n < 7) // 跳过 2,4,6(都小于7),从7开始

.forEach(System.out::print); // 输出:7810

// ofNullable:避免空流

Stream stream = Stream.ofNullable(null);
stream.findFirst().ifPresent(System.out::println); // 无输出(流为空)

三、JDK 10(2018):局部变量类型推断

JDK 10 引入 var 关键字,简化局部变量声明(仅适用于局部变量,不可用于方法参数或字段)。

var 局部变量类型推断

编译器自动推断变量类型,减少冗余代码。

示例:用 var 简化代码

public class VarDemo {

public static void main(String[] args) {

// 传统声明

String name = "张三";

List numbers = new ArrayList<>();

复制代码
    // 使用 var(类型由编译器推断)
    var nameInferred = "张三";       // 推断为 String
    var numbersInferred = new ArrayList<>(); // 推断为 ArrayList<Object>(需注意类型安全)
    var list = List.of(1, 2, 3);     // 推断为 List<Integer>

    // 注意:var 不能用于方法参数或返回值类型
    // public var method(var param) {} // 编译错误
}

}

四、JDK 11(2018):HTTP Client 与字符串增强

JDK 11 是长期支持(LTS)版本,新增 HTTP/2 客户端、字符串工具方法等。

  1. HTTP Client(正式版)

替代传统的 HttpURLConnection,支持 HTTP/1.1 和 HTTP/2,异步非阻塞。

示例:发送 HTTP GET 请求

import java.net.URI;

import java.net.http.HttpClient;

import java.net.http.HttpRequest;

import java.net.http.HttpResponse;

import java.util.concurrent.CompletableFuture;

public class HttpClientDemo {

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

HttpClient client = HttpClient.newHttpClient();

复制代码
    // 同步请求
    HttpRequest request = HttpRequest.newBuilder()
        .uri(URI.create("https://httpbin.org/get"))
        .build();

    HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
    System.out.println("状态码:" + response.statusCode()); // 输出:200
    System.out.println("响应体:" + response.body().substring(0, 50) + "..."); // 输出部分内容

    // 异步请求(返回 CompletableFuture)
    CompletableFuture<HttpResponse<String>> asyncResponse = client.sendAsync(
        request,
        HttpResponse.BodyHandlers.ofString()
    );
    asyncResponse.thenAccept(res -> System.out.println("异步响应状态码:" + res.statusCode()));
}

}

  1. 字符串新增方法

String 类新增 isBlank()、strip()(去除首尾空白)、repeat() 等方法。

示例:字符串操作

String str1 = " \t hello \n ";

String str2 = "";

String str3 = "Java";

System.out.println(str1.isBlank()); // 输出:false(包含非空白字符)

System.out.println(str2.isBlank()); // 输出:true(空字符串或全空白)

System.out.println(str1.strip()); // 输出:"hello"(去除首尾空白)

System.out.println(str1.trim()); // 输出:"hello"(传统方法,不处理 \t/\n)

System.out.println(str3.repeat(3)); // 输出:"JavaJavaJava"

五、JDK 12(2019):Switch 表达式(预览)

JDK 12 引入更简洁的 Switch 表达式(预览版,JDK 14 正式发布),支持箭头语法和 yield 返回值。

Switch 表达式(JDK 14 正式)

用 -> 替代 :,支持直接返回值,避免 break 冗余。

示例:Switch 表达式

int day = 3;

String dayName;

// 传统 Switch(语句,无返回值)

switch (day) {

case 1:

dayName = "周一";

break;

case 2:

dayName = "周二";

break;

case 3:

dayName = "周三"; // 输出:周三

break;

default:

dayName = "未知";

}

// Switch 表达式(JDK 14+,可直接返回值)

dayName = switch (day) {

case 1 -> "周一";

case 2 -> "周二";

case 3 -> "周三"; // 自动返回 "周三"

default -> "未知";

};

// 复杂逻辑用 yield(需用大括号)

int numLetters = switch (dayName) {

case "周一", "周二", "周三", "周四", "周五" -> 2; // 多个值合并

case "周六", "周日" -> {

System.out.println("周末");

yield 3; // 用 yield 返回值

}

default -> throw new IllegalStateException("无效星期");

};

System.out.println("字母数:" + numLetters); // 输出:2(周三对应2)

六、JDK 16(2021):Record 与 Pattern Matching(正式)

JDK 16 是 LTS 版本,正式发布 Record(不可变数据载体)和改进的 instanceof 模式匹配。

  1. Record(正式版)

简化不可变数据类的定义(自动生成 equals()、hashCode()、toString() 等方法)。

示例:用 Record 定义数据类

// 传统方式定义不可变类

class TraditionalUser {

private final String name;

private final int age;

复制代码
public TraditionalUser(String name, int age) {
    this.name = name;
    this.age = age;
}

// 需手动实现 equals、hashCode、toString...

}

// Record 方式(一行代码搞定)

record RecordUser(String name, int age) {}

public class RecordDemo {

public static void main(String[] args) {

RecordUser user = new RecordUser("张三", 25);

System.out.println(user); // 输出:RecordUser[name=张三, age=25](自动生成 toString)

System.out.println(user.name()); // 输出:张三(自动生成访问器 name())

System.out.println(user.age()); // 输出:25

}

}

  1. Pattern Matching for instanceof(正式版)

简化 instanceof 类型检查和强制转换的代码。

示例:模式匹配 instanceof

Object obj = "Hello JDK 16";

// 传统方式

if (obj instanceof String) {

String str = (String) obj; // 需显式强制转换

System.out.println(str.length()); // 输出:11

}

// 模式匹配(JDK 16+)

if (obj instanceof String str) { // 自动转换为 String 类型并赋值给 str

System.out.println(str.length()); // 输出:11(无需显式转换)

}

七、JDK 17(2021):密封类与虚拟线程(孵化)

JDK 17 是 LTS 版本,正式发布密封类(Sealed Classes),并孵化虚拟线程(Virtual Threads)。

  1. 密封类(正式版)

限制类的继承范围(仅允许指定子类继承或实现),增强类型安全性。

示例:密封类与允许的子类

// 密封接口,仅允许 Circle 和 Rectangle 实现

public sealed interface Shape permits Circle, Rectangle {}

// 子类必须声明为 final、sealed 或 non-sealed

final class Circle implements Shape {

private final double radius;

public Circle(double r) { this.radius = r; }

public double area() { return Math.PI * radius * radius; }

}

sealed class Rectangle implements Shape permits Square {} // 子类可进一步限制

final class Square extends Rectangle {

private final double side;

public Square(double s) { this.side = s; }

public double area() { return side * side; }

}

// 模式匹配中可安全使用密封类的子类型

public static double calculateArea(Shape shape) {

return switch (shape) {

case Circle c -> c.area(); // 仅允许 Circle

case Rectangle r -> r.area(); // 仅允许 Rectangle(或其允许的子类 Square)

// 无需 default,因为 Shape 的子类已被完全限制

};

}

public static void main(String[] args) {

Shape circle = new Circle(5);

Shape square = new Square(4);

System.out.println(calculateArea(circle)); // 输出:78.5398...

System.out.println(calculateArea(square)); // 输出:16.0

}

  1. 虚拟线程(孵化,JDK 21 正式)

轻量级线程(用户态线程),大幅降低高并发场景的资源消耗(如百万级连接)。

示例:虚拟线程执行任务

public class VirtualThreadDemo {

public static void main(String[] args) {

// 创建 10 个虚拟线程

try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {

for (int i = 0; i < 10; i++) {

int taskId = i;

executor.submit(() -> {

System.out.println("任务 " + taskId + " 运行在:" + Thread.currentThread());

// 模拟耗时操作

Thread.sleep(Duration.ofSeconds(1));

return taskId;

});

}

} catch (InterruptedException e) {

Thread.currentThread().interrupt();

}

}

}

// 输出类似:

// 任务 0 运行在:VirtualThread[#1]/runnable@123456

// 任务 1 运行在:VirtualThread[#2]/runnable@7890ab

// ...(每个任务几乎同时启动)

八、JDK 21(2023):虚拟线程正式版与字符串模板

JDK 21 是 LTS 版本,虚拟线程正式发布,并新增字符串模板(String Templates)。

  1. 虚拟线程(正式版)

如前所述,虚拟线程是 JDK 21 的核心特性之一,适用于高并发 I/O 密集型场景(如 Web 服务器、数据库连接)。

  1. 字符串模板(正式版)

简化字符串拼接,支持表达式嵌入和安全转义(类似 Python 的 f-string)。

示例:字符串模板

// 定义模板(使用 StringTemplate)

String name = "张三";

int age = 25;

String message = STR."""

用户信息:

姓名:{name}

年龄:{age}

""";

System.out.println(message);

// 输出:

// 用户信息:

// 姓名:张三

// 年龄:25

  1. 外国函数接口(FFI,正式版)

通过 jdk.incubator.foreign 包安全调用本地代码(如 C/C++),替代 JNI(Java Native Interface)。

示例:调用 C 的 strlen 函数

import jdk.incubator.foreign.;
import static jdk.incubator.foreign.CLinker.
;

public class FFIExample {

public static void main(String[] args) {

try (Arena arena = Arena.ofConfined()) {

// 在本地内存中分配字符串(以 null 结尾)

MemorySegment str = arena.allocateUtf8String("Hello FFI");

复制代码
        // 获取 C 的 strlen 函数指针
        MemorySegment strlenFunc = CLinker.getInstance().lookup("strlen").get();
        
        // 调用 strlen(参数:C指针)
        long length = (long) CLinker.invokeExact(
            strlenFunc, 
            FunctionDescriptor.of(CLong, CPointer), 
            str.address()
        );
        
        System.out.println("字符串长度:" + length); // 输出:10("Hello FFI" 长度为10)
    }
}

}

总结

JDK 8 后的版本持续聚焦开发效率(Lambda、Stream、Record)、性能优化(向量API、ZGC)、现代化API(日期时间、HTTP Client)和并发模型革新(虚拟线程)。实际项目中,建议优先使用 LTS 版本(如 JDK 8、11、17、21),并根据需求选择特性(如用 Record 简化数据类,用虚拟线程处理高并发)。

相关推荐
Grey Zeng2 天前
Java SE 25新增特性
java·jdk·jdk新特性·jdk25
Grey Zeng6 个月前
Java SE 24 新增特性
java·jdk·jdk新特性·jdk24
Grey Zeng1 年前
Java SE 23 新增特性
java·jdk·jdk新特性·jdk23
Grey Zeng1 年前
Java SE 22 新增特性
java·jdk·jdk新特性·jdk22
Grey Zeng2 年前
Java SE 21 新增特性
java·jdk新特性·jdk21