1. 概述
在技术选型时,我们常面临两难选择:是升级到新版Java,还是继续使用现有版本?这本质上是在权衡新功能带来的收益与迁移成本之间的平衡。
本文将介绍Java 8到Java 17之间那些极其实用的新特性。这些特性不仅学习成本低,而且能快速落地,显著降低迁移难度。
2. String类增强
String类在后续版本中获得了多项重要改进。
2.1. 字符串压缩机制
Java 9引入了字符串压缩特性,通过优化内存表示提升性能。
核心变化:String对象内部由char[]
改为byte[]
存储。因为Java内部使用UTF-16编码,每个char
占用2字节。而实际场景中,多数字符串(如英文)仅需1字节(LATIN-1编码)即可表示。
Java会根据字符串内容自动选择存储方式:
- 纯LATIN-1字符 →
byte[]
- 包含特殊字符 →
char[]
开发者无需关心底层实现,完全透明。对比内部字段变化:
// Java 8及之前
char[] value;
// Java 9+
byte[] value;
2.2. 文本块
Java 15之前,处理多行文本需要手动拼接换行符和字符串。文本块(Text Blocks)的引入彻底解决了这个痛点,特别适合嵌入HTML/JSON/SQL等代码片段。
文本块是String字面量的替代形式,使用三引号定义:
// 使用文本块
String value = """
Multi-line
Text
""";
传统方式则繁琐易错:
// 使用传统字符串字面量
String value = "Multi-line"
+ "\n" // 换行符
"Text"
+ "\n";
String str = "Multi-line\nText\n";
2.3. 新增String方法
过去处理字符串常依赖Apache Commons等第三方库。Java 11/12新增了多个实用方法,减少外部依赖:
✅ isBlank()
- 判断空白字符串
✅ repeat()
- 重复字符串
✅ indent()
- 添加缩进
✅ lines()
- 按行分割
✅ strip()
- 去除首尾空白
✅ transform()
- 链式转换
实际用法:
assertThat(" ".isBlank());
assertThat("Twinkle ".repeat(2)).isEqualTo("Twinkle Twinkle ");
assertThat("Format Line".indent(4)).isEqualTo(" Format Line\n");
assertThat("Line 1 \n Line2".lines()).asList().size().isEqualTo(2);
assertThat(" Text with white spaces ".strip()).isEqualTo("Text with white spaces");
assertThat("Car, Bus, Train".transform(s1 -> Arrays.asList(s1.split(","))).get(0)).isEqualTo("Car");
3. 记录类(Records)
DTO(数据传输对象)需要大量模板代码:字段、构造器、getter/setter、equals()
、hashCode()
、toString()
等。
public class StudentDTO {
private int rollNo;
private String name;
// 构造器
// getter/setter
// equals(), hashCode(), toString()
}
记录类(Records)提供了一种极简的不可变数据类定义方式,效果类似Lombok。Java 14预览,Java 16转正:
public record Student(int rollNo, String name) {
}
编译器自动生成:
- 全参构造器
- private final字段
- 访问方法(非getter)
equals()
/hashCode()
/toString()
使用示例:
Student student = new Student(10, "Priya");
Student student2 = new Student(10, "Priya");
assertThat(student.rollNo()).isEqualTo(10);
assertThat(student.name()).isEqualTo("Priya");
assertThat(student.equals(student2));
assertThat(student.hashCode()).isEqualTo(student2.hashCode());
4. 更友好的NullPointerException
NPE是Java开发者最常遇到的异常。传统错误信息难以定位具体空对象,尤其在链式调用中:
student.getAddress().getCity().toLowerCase();
当NPE发生时,很难快速判断是student
、getAddress()
还是getCity()
为null。
Java 14引入了增强型NPE提示,需添加JVM参数:
-XX:+ShowCodeDetailsInExceptionMessages
启用后错误信息更精确:
Cannot invoke "String.toLowerCase()" because the return value of "com.baeldung.java8to17.Address.getCity()" is null
⚠️ Java 15起该参数默认开启
5. 模式匹配
模式匹配简化了"条件判断+类型转换+变量绑定"的常见操作,使代码更简洁安全。
5.1. 增强的instanceof操作符
传统写法需要三步:类型检查 → 强制转换 → 变量赋值:
if (obj instanceof Address) {
Address address = (Address) obj;
city = address.getCity();
}
Java 16的模式匹配直接在判断中完成类型转换和变量绑定:
if (obj instanceof Address address) {
city = address.getCity();
}
5.2. Switch表达式
Switch表达式(Java 14)可返回值,支持任意类型。Java 17进一步支持模式匹配(预览特性):
double circumference = switch(shape) {
case Rectangle r -> 2 * r.length() + 2 * r.width();
case Circle c -> 2 * c.radius() * Math.PI;
default -> throw new IllegalArgumentException("Unknown shape");
};
关键改进:
- 使用
case L ->
替代case L:
- 无需
break
防止穿透 - 支持任意类型选择器
- 传统
case L:
需用yield
返回值
6. 密封类(Sealed Classes)
某些业务场景需要限制继承范围(如领域驱动设计)。密封类通过明确许可的子类列表,实现可控继承。
定义密封类:
public sealed class Shape permits Circle, Square, Triangle {
}
子类必须使用以下修饰符之一:
final
- 不可再继承sealed
- 可继续密封non-sealed
- 开放继承
示例:
public final class Circle extends Shape {
public float radius;
}
public non-sealed class Square extends Shape {
public double side;
}
public sealed class Triangle extends Shape permits ColoredTriangle {
public double height, base;
}
7. 总结
从Java 8到Java 17,Java通过渐进式迭代引入了大量新特性。本文精选的特性学习成本低、见效快,特别是:
- String类增强
- 记录类
- 模式匹配
- 密封类
这些改进能显著提升开发效率、代码可读性和系统性能,是Java 8迁移到Java 17的绝佳切入点。
本文所有代码示例可在GitHub仓库获取。