⭐⭐⭐ Spring Boot 项目实战 ⭐⭐⭐ Spring Cloud 项目实战
《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 juejin.cn/post/7506039220218707995 「技术小馆」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注微信公众号:【芋道源码】有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言都将得到认真回复。甚至不知道如何读源码也可以请教噢。
  4. 新的源码解析文章实时收到通知。每周更新一篇左右。
  5. 认真的源码交流微信群。

还在用 Java 8 写代码?是时候升级你的技能库了!JDK 17 作为一个长期支持版本,带来了一系列令人惊艳的语法特性,让 Java 编程变得更加简洁高效。

从优雅的密封类到简洁的记录类,从模式匹配到文本块,这些”神仙语法”不仅能让你的代码行数减少 30%,还能提高代码可读性和维护性。作为一位使用 JDK 17 已有两年的开发者,我将分享这些强大特性如何在实际项目中为我节省了无数时间,并彻底改变了我编写 Java 代码的方式。准备好迎接 Java 的新时代了吗?

一、从 JDK 8 到 JDK 17

为什么 JDK 17 是里程碑版本

JDK 17 不仅仅是一个普通的版本更新,它标志着 Java 平台的重大转变。作为继 JDK 8 和 JDK 11 之后的又一个长期支持(LTS)版本,JDK 17 集成了自 JDK 9 以来的所有创新特性,是 Java 现代化进程中的重要里程碑。

长期支持版本的意义

作为 LTS 版本,JDK 17 将获得至少 8 年的支持期。这意味着企业可以安心地将其应用迁移到 JDK 17 上,享受新特性带来的便利,同时不必担心频繁升级的问题。对于那些仍在使用 JDK 8 的开发者来说,直接跳到 JDK 17 是一个明智的选择。

二、记录类(Record)

传统 JavaBean 的痛点

在传统 Java 开发中,创建一个简单的数据类需要编写大量模板代码:

public class Person {
privatefinal String name;
privatefinalint age;

public Person(String name, int age) {
this.name = name;
this.age = age;
}

public String getName() { return name; }
public int getAge() { return age; }

@Override
public boolean equals(Object o) {
// 长长的equals实现...
}

@Override
public int hashCode() {
// hashCode实现...
}

@Override
public String toString() {
return"Person[name=" + name + ", age=" + age + "]";
}
}

这种冗长的代码不仅编写麻烦,而且容易出错,更重要的是它掩盖了类的本质意图。

Record 的基本语法与使用

JDK 17 中的记录类(Record)彻底解决了这个问题:

public record Person(String name, int age) {}

就这么简单!编译器会自动为你生成构造器、getter 方法、equals()、hashCode() 和 toString() 方法。这一行代码等同于前面的几十行代码,简直是偷懒神器!

Record 与不可变对象

Record 天生是不可变的,这符合函数式编程的理念,有助于编写线程安全的代码。如果你需要修改记录的某个字段,只能创建一个新的实例:

Person olderAlice = new Person(alice.name(), alice.age() + 1);

何时使用与不使用 Record

Record 非常适合作为 DTO(数据传输对象)、值对象或不可变数据容器。但它也有局限性:不能继承其他类,不能声明实例字段(除了在构造函数中定义的),不能是抽象的。如果你需要这些特性,还是应该使用传统类。

三、密封类(Sealed Classes)

密封类的核心概念

在 Java 中,一个类要么是 final 的(不能被继承),要么可以被任何类继承。密封类(Sealed Classes)提供了一种中间状态:你可以指定哪些类可以继承它。

public sealed class Shape permits Circle, Rectangle, Triangle {
// 共享方法和属性
}

permits 关键字详解

permits 关键字明确列出了允许继承该密封类的所有子类。子类必须使用 final、sealed 或 non-sealed 修饰符来声明自己的继承策略:

public final class Circle extends Shape {
// Circle不能再被继承
}

public sealed class Rectangle extends Shape permits Square {
// Rectangle只能被Square继承
}

public non-sealed class Triangle extends Shape {
// Triangle可以被任何类继承
}

与接口结合使用

密封特性也适用于接口:

public sealed interface Vehicle permits Car, Truck, Motorcycle {
void move();
}

实际应用案例

密封类非常适合领域建模,特别是当你有一个封闭的类型集合时:

public sealed interface PaymentMethod permits CreditCard, DebitCard, BankTransfer, DigitalWallet {
boolean processPayment(double amount);
}

publicfinalclass CreditCard implements PaymentMethod {
@Override
public boolean processPayment(double amount) {
// 信用卡支付逻辑
returntrue;
}
}

// 其他实现类...

这样,当你使用 switch 语句处理不同的支付方式时,编译器可以确保你已经处理了所有可能的情况。

四、模式匹配

类型模式匹配

在 JDK 17 之前,使用 instanceof 进行类型检查后,我们还需要显式地进行类型转换:

// 旧方式
if (obj instanceof String) {
String s = (String) obj;
if (s.length() > 5) {
// 使用字符串 s
}
}

JDK 17 引入了模式匹配,可以在 instanceof 中直接绑定变量:

// 新方式
if (obj instanceof String s && s.length() > 5) {
// 直接使用字符串 s
}

简洁明了,一气呵成!

switch 表达式增强

JDK 17 中的 switch 也支持了模式匹配:

Object obj = getSomeObject();
String result = switch (obj) {
case Integer i -> "整数: " + i;
case String s -> "字符串: " + s;
case Person p -> "人: " + p.name();
default -> "未知类型";
};

性能考量

模式匹配不仅提高了代码可读性,而且在许多情况下还能提升性能,因为编译器可以对模式匹配进行优化,减少冗余的类型检查。

五、文本块

传统字符串拼接的问题

在 JDK 15 之前,处理多行字符串是一件痛苦的事情:

String html = "<html>\n" +
" <body>\n" +
" <h1>Hello, World!</h1>\n" +
" </body>\n" +
"</html>";

这种代码不仅难以维护,而且容易出错。

文本块语法详解

JDK 17 中的文本块(Text Blocks)让多行字符串变得简单:

String html = """
<html>
<body>
<h1>Hello, World!</h1>
</body>
</html>
""";

文本块以三个双引号开始和结束,中间的内容可以包含任意字符,包括换行符和引号,无需转义。

格式控制技巧

文本块会自动删除每行开头的公共空白,但你可以通过 \s 来保留空格,或使用 来连接行:

String query = """
SELECT id, name, email \
FROM users \
WHERE status = 'ACTIVE' \
ORDER BY name""";

JSON、SQL 和 HTML 处理实例

文本块特别适合处理结构化文本:

// JSON示例
String jsonConfig = """
{
"appName": "神仙应用",
"version": "1.0.0",
"features": [
"记录类",
"密封类",
"模式匹配"
]
}
""";

// SQL示例
String sql = """
SELECT p.name, p.age, a.city
FROM persons p
JOIN addresses a ON p.id = a.person_id
WHERE a.country = '中国'
AND p.age > 18
""";

六、var 与增强型 switch

类型推断的魅力

虽然 var 是在 JDK 10 中引入的,但它与 JDK 17 的其他特性结合使用时,可以让代码更加简洁:

// 不使用var
Map<String, List<Person>> groupedPeople = new HashMap<>();

// 使用var
var groupedPeople = new HashMap<String, List<Person>>();

switch 表达式与 yield

JDK 17 中的 switch 可以作为表达式使用,并且可以直接返回值:

int dayOfWeek = 3;
String day = switch (dayOfWeek) {
case1 -> "星期一";
case2 -> "星期二";
case3 -> "星期三";
case4 -> "星期四";
case5 -> "星期五";
case6, 7 -> "周末";
default -> "无效日期";
};

如果需要更复杂的逻辑,可以使用代码块和 yield 关键字:

String result = switch (status) {
case "PENDING" -> {
log.info("处理待定状态");
yield "处理中";
}
case "APPROVED" -> {
log.info("处理已批准状态");
yield "已完成";
}
default -> "未知状态";
};

箭头语法与多分支处理

新的 switch 语法支持使用箭头 -> 来简化代码,并且可以在一个 case 中处理多个值:

Season season = switch (month) {
case 3, 4, 5 -> Season.SPRING;
case 6, 7, 8 -> Season.SUMMER;
case 9, 10, 11 -> Season.AUTUMN;
case 12, 1, 2 -> Season.WINTER;
default -> throw new IllegalArgumentException("无效月份");
};

代码可读性的平衡

虽然这些新特性可以让代码更简洁,但也要注意不要过度使用,导致代码难以理解。保持适度,让代码既简洁又清晰。

七、其他实用特性大集合

私有接口方法

从 JDK 9 开始,接口可以包含私有方法,这在实现默认方法时非常有用:

public interface Logger {
default void logInfo(String message) {
log(message, "INFO");
}

default void logError(String message) {
log(message, "ERROR");
}

// 私有辅助方法
private void log(String message, String level) {
System.out.println("[" + level + "] " + message);
}
}

改进的 Stream API

JDK 17 中的 Stream API 增加了一些实用方法:

// 将流转换为List(不需要再调用collect(Collectors.toList()))
List<String> names = people.stream()
.map(Person::name)
.filter(name -> name.startsWith("张"))
.toList();

// 新的mapMulti方法,可以为每个元素生成多个结果
List<String> words = sentences.stream()
.mapMulti((sentence, consumer) -> {
for (String word : sentence.split(" ")) {
consumer.accept(word);
}
})
.toList();

增强的 NullPointerException

JDK 17 中的 NullPointerException 会提供更详细的错误信息,指出哪个变量是 null:

// 旧版本的错误信息
Exception in thread "main" java.lang.NullPointerException

// JDK 17 的错误信息
Exception in thread "main" java.lang.NullPointerException:
Cannot invoke "Person.getName()" because "person" is null

这大大提高了调试效率,不再需要猜测哪个对象是 null。

新的垃圾收集器

JDK 17 提供了多种垃圾收集器选项,包括 ZGC(Z Garbage Collector),它能够处理 TB 级别的堆内存,同时保持低于 10ms 的暂停时间:

// 启用ZGC的JVM参数
-XX:+UseZGC

外部内存访问 API

JDK 17 引入了外部内存访问 API,允许 Java 程序安全地访问堆外内存:

// 分配堆外内存
try (MemorySegment segment = MemorySegment.allocateNative(100)) {
// 写入数据
MemoryAccess.setInt(segment, 0, 42);

// 读取数据
int value = MemoryAccess.getInt(segment, 0);
System.out.println(value); // 输出: 42
}

这对于需要与本地代码交互或处理大量数据的应用程序特别有用。

JDK 17 带来的这些”神仙语法”不仅让 Java 代码更加简洁优雅,还提高了开发效率和运行性能。作为一名 Java 开发者,掌握这些新特性将让你在编码过程中如虎添翼。是时候告别那些冗长的老式 Java 代码,拥抱现代 Java 编程的美好未来了!

文章目录
  1. 1. 一、从 JDK 8 到 JDK 17
    1. 1.0.1. 为什么 JDK 17 是里程碑版本
    2. 1.0.2. 长期支持版本的意义
  • 2. 二、记录类(Record)
    1. 2.0.1. 传统 JavaBean 的痛点
    2. 2.0.2. Record 的基本语法与使用
    3. 2.0.3. Record 与不可变对象
    4. 2.0.4. 何时使用与不使用 Record
  • 3. 三、密封类(Sealed Classes)
    1. 3.0.1. 密封类的核心概念
    2. 3.0.2. permits 关键字详解
    3. 3.0.3. 与接口结合使用
    4. 3.0.4. 实际应用案例
  • 4. 四、模式匹配
    1. 4.0.1. 类型模式匹配
    2. 4.0.2. switch 表达式增强
    3. 4.0.3. 性能考量
  • 5. 五、文本块
    1. 5.0.1. 传统字符串拼接的问题
    2. 5.0.2. 文本块语法详解
    3. 5.0.3. 格式控制技巧
    4. 5.0.4. JSON、SQL 和 HTML 处理实例
  • 6. 六、var 与增强型 switch
    1. 6.0.1. 类型推断的魅力
    2. 6.0.2. switch 表达式与 yield
    3. 6.0.3. 箭头语法与多分支处理
    4. 6.0.4. 代码可读性的平衡
  • 7. 七、其他实用特性大集合
    1. 7.0.1. 私有接口方法
    2. 7.0.2. 改进的 Stream API
    3. 7.0.3. 增强的 NullPointerException
    4. 7.0.4. 新的垃圾收集器
    5. 7.0.5. 外部内存访问 API