第四天:Java 13到Java 16新特性
今天我们将学习Java 13到Java 16的新特性。这些版本引入了许多实验性功能,其中一些在后续版本中得到了标准化。
Java 13新特性
Java 13于2019年9月发布,继续改进了一些预览功能并引入了新的特性。
1. 文本块(预览)
Java 13引入了文本块作为预览功能,使多行字符串的表示更加简洁和直观。
// Java 12及之前
String json = "{\n" +
" \"name\": \"John Doe\",\n" +
" \"age\": 30,\n" +
" \"address\": {\n" +
" \"street\": \"123 Main St\",\n" +
" \"city\": \"Anytown\"\n" +
" }\n" +
"}";
// Java 13(预览)
String json = """
{
"name": "John Doe",
"age": 30,
"address": {
"street": "123 Main St",
"city": "Anytown"
}
}
""";
+------------------+ +------------------+
| 传统字符串 | | 文本块 |
+------------------+ +------------------+
| "line1\n" + | | """ |
| "line2\n" + | | line1 |
| "line3" | | line2 |
| | | line3 |
| | | """ |
+------------------+ +------------------+
2. Switch表达式更新(预览)
Java 13更新了Switch表达式,引入了yield语句来返回值。
// Java 12
int numLetters = switch (day) {
case MONDAY, FRIDAY, SUNDAY -> 6;
case TUESDAY -> 7;
case THURSDAY, SATURDAY -> 8;
case WEDNESDAY -> 9;
default -> 0;
};
// Java 13
int numLetters = switch (day) {
case MONDAY, FRIDAY, SUNDAY -> 6;
case TUESDAY -> 7;
case THURSDAY, SATURDAY -> 8;
case WEDNESDAY -> 9;
default -> {
System.out.println("Unknown day: " + day);
yield 0;
}
};
+------------------+ +------------------+
| Java 12 Switch | | Java 13 Switch |
+------------------+ +------------------+
| case MONDAY -> | | case MONDAY -> |
| 6; | | 6; |
| | | |
| default -> 0; | | default -> { |
| | | System.out. |
| | | println(...); |
| | | yield 0; |
| | | } |
+------------------+ +------------------+
3. 重新实现Socket API
Java 13重新实现了Socket API,使用NIO实现,提高了可维护性和性能。
// 旧的Socket API
Socket socket = new Socket("example.com", 80);
OutputStream out = socket.getOutputStream();
InputStream in = socket.getInputStream();
// 新的Socket API(内部实现变化,API保持不变)
Socket socket = new Socket("example.com", 80);
OutputStream out = socket.getOutputStream();
InputStream in = socket.getInputStream();
+------------------+ +------------------+
| 旧Socket实现 | | 新Socket实现 |
+------------------+ +------------------+
| 遗留代码 | | NIO实现 |
| 难以维护 | | 更易维护 |
| 性能较低 | | 性能更好 |
| 不支持异步操作 | | 支持异步操作 |
+------------------+ +------------------+
4. ZGC:取消提交未使用内存
Java 13改进了ZGC垃圾收集器,使其能够将未使用的内存返回给操作系统。
# 启用ZGC并允许取消提交未使用内存
java -XX:+UnlockExperimentalVMOptions -XX:+UseZGC -XX:+ZUncommit -jar app.jar
+------------------+ +------------------+
| ZGC改进前 | | ZGC改进后 |
+------------------+ +------------------+
| 分配内存 | | 分配内存 |
| 回收垃圾 | | 回收垃圾 |
| 保留所有内存 | | 取消提交未使用 |
| | | 内存 |
+------------------+ +------------------+
5. 其他Java 13特性
- 动态CDS归档:增强了类数据共享(CDS)功能,允许在应用程序执行结束时动态归档类。
- FileSystems.newFileSystem()方法:添加了新的工厂方法来创建FileSystem对象。
Java 14新特性
Java 14于2020年3月发布,引入了多个新特性和预览功能。
1. Switch表达式(标准化)
Java 14将Switch表达式标准化,不再是预览功能。
// Java 14标准化的Switch表达式
int numLetters = switch (day) {
case MONDAY, FRIDAY, SUNDAY -> 6;
case TUESDAY -> 7;
case THURSDAY, SATURDAY -> 8;
case WEDNESDAY -> 9;
default -> {
System.out.println("Unknown day: " + day);
yield 0;
}
};
+------------------+ +------------------+
| 传统Switch语句 | | Switch表达式 |
+------------------+ +------------------+
| 语句形式 | | 表达式形式 |
| 需要break语句 | | 不需要break语句 |
| 容易出错 | | 更安全 |
| 不能直接赋值 | | 可以直接赋值 |
+------------------+ +------------------+
2. 记录类(预览)
Java 14引入了记录类(Record)作为预览功能,用于创建不可变的数据类。
// Java 14(预览)
record Point(int x, int y) {
// 自动生成:
// - 构造函数
// - 访问器方法 x() 和 y()
// - equals() 和 hashCode()
// - toString()
// 可以添加静态字段和方法
public static Point origin() {
return new Point(0, 0);
}
// 可以添加实例方法
public double distanceFromOrigin() {
return Math.sqrt(x * x + y * y);
}
// 可以自定义构造函数
public Point {
if (x < 0 || y < 0) {
throw new IllegalArgumentException("Coordinates cannot be negative");
}
}
}
// 使用记录类
Point p = new Point(3, 4);
System.out.println(p); // Point[x=3, y=4]
System.out.println(p.x()); // 3
System.out.println(p.y()); // 4
System.out.println(p.distanceFromOrigin()); // 5.0
+------------------+ +------------------+
| 传统数据类 | | 记录类 |
+------------------+ +------------------+
| class Point { | | record Point( |
| private final | | int x, |
| int x, y; | | int y |
| // 构造函数 | | ) { |
| // getter | | // 自动生成 |
| // equals() | | // 构造函数 |
| // hashCode() | | // 访问器 |
| // toString() | | // equals() |
| } | | // hashCode() |
| | | // toString() |
+------------------+ +------------------+
3. 模式匹配for instanceof(预览)
Java 14引入了instanceof的模式匹配作为预览功能,简化了类型检查和转换。
// Java 14之前
if (obj instanceof String) {
String s = (String) obj;
System.out.println(s.length());
}
// Java 14(预览)
if (obj instanceof String s) {
System.out.println(s.length());
}
+------------------+ +------------------+
| 传统instanceof | | 模式匹配instanceof|
+------------------+ +------------------+
| if (obj instanceof| | if (obj instanceof|
| String) { | | String s) { |
| String s = | | |
| (String) obj; | | |
| ... | | ... |
| } | | } |
+------------------+ +------------------+
4. 有用的NullPointerException
Java 14改进了NullPointerException的错误消息,提供了更详细的信息,帮助开发者更快地定位问题。
// Java 14之前
person.getAddress().getStreet(); // 如果getAddress()返回null
// NullPointerException
// Java 14
person.getAddress().getStreet();
// NullPointerException: Cannot invoke "Address.getStreet()" because the return value of "Person.getAddress()" is null
+------------------+ +------------------+
| 传统NPE消息 | | 增强NPE消息 |
+------------------+ +------------------+
| NullPointer | | NullPointer |
| Exception | | Exception: |
| | | Cannot invoke |
| | | "Address.get |
| | | Street()" because|
| | | the return value |
| | | of "Person.get |
| | | Address()" is null|
+------------------+ +------------------+
5. 打包工具(孵化器)
Java 14引入了jpackage工具作为孵化器功能,用于打包自包含的Java应用程序。
# 创建可执行文件
jpackage --name myapp --input lib --main-jar myapp.jar
# 创建安装程序
jpackage --name myapp --input lib --main-jar myapp.jar --type dmg
+------------------+ +------------------+
| jpackage流程 | | |
+------------------+ | |
| Java应用程序 | | |
| | | | |
| v | | |
| jpackage工具 | | |
| | | | |
| v | | |
| 平台特定安装包 | | |
| (exe, dmg, deb, | | |
| rpm等) | | |
+------------------+ +------------------+
6. 非易失性映射字节缓冲区
Java 14增强了MappedByteBuffer,添加了非易失性内存支持。
// 创建非易失性映射字节缓冲区
FileChannel fileChannel = FileChannel.open(path, StandardOpenOption.READ, StandardOpenOption.WRITE);
MappedByteBuffer buffer = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0, size);
7. 其他Java 14特性
- 删除CMS垃圾收集器:移除了CMS(Concurrent Mark Sweep)垃圾收集器。
- 弃用ParallelScavenge + SerialOld GC组合:标记为弃用。
- ZGC在macOS和Windows上可用:ZGC现在可以在macOS和Windows上使用。
- 外部存储器访问API(孵化器):引入了用于访问Java堆外内存的API。
Java 15新特性
Java 15于2020年9月发布,继续改进了一些预览功能并引入了新的特性。
1. 文本块(标准化)
Java 15将文本块标准化,不再是预览功能。
// Java 15标准化的文本块
String html = """
<html>
<body>
<p>Hello, World!</p>
</body>
</html>
""";
2. 密封类(预览)
Java 15引入了密封类(Sealed Classes)作为预览功能,允许类的作者控制哪些类可以继承该类。
// Java 15(预览)
public sealed class Shape permits Circle, Rectangle, Square {
// ...
}
public final class Circle extends Shape {
// ...
}
public non-sealed class Rectangle extends Shape {
// ...
}
public final class Square extends Rectangle {
// ...
}
// 尝试创建未经许可的子类
public class Triangle extends Shape { // 编译错误:Triangle不在permits列表中
// ...
}
+------------------+ +------------------+
| 密封类层次结构 | | |
+------------------+ | |
| Shape | | |
| (sealed) | | |
| / | \ | | |
| / | \ | | |
| Circle Rectangle | | |
|(final)(non-sealed)| | |
| | | | |
| | | | |
| Square | | |
| (final) | | |
+------------------+ +------------------+
3. 隐藏类
Java 15引入了隐藏类,这些类不能被普通字节码直接使用,主要用于框架生成运行时类。
// 创建隐藏类
byte[] classBytes = ...; // 类的字节码
Class<?> hiddenClass = Lookup.defineHiddenClass(classBytes, false, ClassOption.NESTMATE).lookupClass();
// 使用隐藏类
Object instance = hiddenClass.newInstance();
Method method = hiddenClass.getMethod("someMethod");
method.invoke(instance);
+------------------+ +------------------+
| 普通类 | | 隐藏类 |
+------------------+ +------------------+
| 可以通过名称引用 | | 不能通过名称引用 |
| 可以被反射访问 | | 有限的反射访问 |
| 长期存在 | | 可以被卸载 |
| 可以被任何代码 | | 只能被创建它的 |
| 访问 | | 代码访问 |
+------------------+ +------------------+
4. 记录类和模式匹配的第二次预览
Java 15对记录类和instanceof的模式匹配进行了第二次预览,进一步改进了这些功能。
5. 爱德华兹曲线算法
Java 15引入了爱德华兹曲线(Edwards-Curve)数字签名算法实现。
// 生成密钥对
KeyPairGenerator kpg = KeyPairGenerator.getInstance("Ed25519");
KeyPair kp = kpg.generateKeyPair();
PrivateKey privateKey = kp.getPrivate();
PublicKey publicKey = kp.getPublic();
// 签名
Signature sig = Signature.getInstance("Ed25519");
sig.initSign(privateKey);
sig.update(data);
byte[] signature = sig.sign();
// 验证
sig.initVerify(publicKey);
sig.update(data);
boolean valid = sig.verify(signature);
+------------------+ +------------------+
| 传统签名算法 | | Ed25519算法 |
+------------------+ +------------------+
| RSA, DSA, ECDSA | | Edwards-Curve |
| 较慢 | | 更快 |
| 密钥较大 | | 密钥较小 |
| 安全性较低 | | 安全性较高 |
+------------------+ +------------------+
6. 废弃偏向锁
Java 15废弃了偏向锁(Biased Locking),计划在未来版本中移除。
# 禁用偏向锁
java -XX:-UseBiasedLocking -jar app.jar
7. ZGC生产就绪
Java 15将ZGC从实验性功能升级为产品功能,可以在生产环境中使用。
# 启用ZGC
java -XX:+UseZGC -jar app.jar
+------------------+ +------------------+
| ZGC特点 | | 优势 |
+------------------+ +------------------+
| 低延迟垃圾收集器 | | 暂停时间<1ms |
| 并发处理 | | 不影响应用程序 |
| | | 线程 |
| 可扩展 | | 从8MB到16TB堆 |
| 彩色指针 | | 高效内存管理 |
+------------------+ +------------------+
8. Shenandoah GC生产就绪
Java 15将Shenandoah GC从实验性功能升级为产品功能,可以在生产环境中使用。
# 启用Shenandoah GC
java -XX:+UseShenandoahGC -jar app.jar
9. 其他Java 15特性
- 外部存储器访问API(第二次孵化):继续改进外部存储器访问API。
- DatagramSocket API重新实现:重新实现了DatagramSocket API。
- 禁用和废弃偏向锁:默认禁用偏向锁,并将其标记为废弃。
Java 16新特性
Java 16于2021年3月发布,继续改进了一些预览功能并引入了新的特性。
1. 记录类(标准化)
Java 16将记录类标准化,不再是预览功能。
// Java 16标准化的记录类
record Point(int x, int y) {
// 自动生成:
// - 构造函数
// - 访问器方法 x() 和 y()
// - equals() 和 hashCode()
// - toString()
// 可以添加静态字段和方法
public static Point origin() {
return new Point(0, 0);
}
// 可以添加实例方法
public double distanceFromOrigin() {
return Math.sqrt(x * x + y * y);
}
// 可以自定义构造函数
public Point {
if (x < 0 || y < 0) {
throw new IllegalArgumentException("Coordinates cannot be negative");
}
}
}
2. 模式匹配for instanceof(标准化)
Java 16将instanceof的模式匹配标准化,不再是预览功能。
// Java 16标准化的instanceof模式匹配
if (obj instanceof String s) {
System.out.println(s.length());
}
3. 密封类(第二次预览)
Java 16对密封类进行了第二次预览,进一步改进了这个功能。
// Java 16(第二次预览)
public sealed class Shape permits Circle, Rectangle, Square {
// ...
}
4. 向量API(孵化器)
Java 16引入了向量API作为孵化器功能,用于表达向量计算。
// Java 16(孵化器)
// 使用向量API进行SIMD操作
IntVector a = IntVector.fromArray(IntVector.SPECIES_128, new int[]{1, 2, 3, 4}, 0);
IntVector b = IntVector.fromArray(IntVector.SPECIES_128, new int[]{5, 6, 7, 8}, 0);
IntVector c = a.add(b);
// c包含[6, 8, 10, 12]
+------------------+ +------------------+
| 标量计算 | | 向量计算 |
+------------------+ +------------------+
| for (int i = 0; | | IntVector a = |
| i < arr.length;| | IntVector.from |
| i++) { | | Array(...); |
| result[i] = | | IntVector b = |
| arr1[i] + | | IntVector.from |
| arr2[i]; | | Array(...); |
| } | | IntVector c = |
| | | a.add(b); |
+------------------+ +------------------+
5. 外部存储器访问API(孵化器)
Java 16继续改进外部存储器访问API,这是第三次孵化。
// Java 16(孵化器)
// 分配堆外内存
MemorySegment segment = MemorySegment.allocateNative(100);
// 写入数据
segment.set(ValueLayout.JAVA_INT, 0, 42);
// 读取数据
int value = segment.get(ValueLayout.JAVA_INT, 0);
// 释放内存
segment.close();
+------------------+ +------------------+
| 传统堆外内存 | | 外部存储器访问API|
+------------------+ +------------------+
| ByteBuffer. | | MemorySegment. |
| allocateDirect() | | allocateNative() |
| 有限的类型支持 | | 丰富的类型支持 |
| 手动管理内存 | | 自动释放资源 |
| 容易出错 | | 更安全 |
+------------------+ +------------------+
6. 打包工具(孵化器)
Java 16继续改进jpackage工具,这是第二次孵化。
# 创建可执行文件
jpackage --name myapp --input lib --main-jar myapp.jar
# 创建安装程序
jpackage --name myapp --input lib --main-jar myapp.jar --type dmg
7. Unix域套接字
Java 16添加了对Unix域套接字(Unix Domain Socket)的支持。
// 创建Unix域套接字服务器
UnixDomainSocketAddress address = UnixDomainSocketAddress.of("/tmp/server.sock");
ServerSocketChannel serverChannel = ServerSocketChannel.open(StandardProtocolFamily.UNIX);
serverChannel.bind(address);
SocketChannel channel = serverChannel.accept();
// 创建Unix域套接字客户端
SocketChannel clientChannel = SocketChannel.open(StandardProtocolFamily.UNIX);
clientChannel.connect(address);
+------------------+ +------------------+
| TCP/IP套接字 | | Unix域套接字 |
+------------------+ +------------------+
| 网络通信 | | 同一主机通信 |
| 较高开销 | | 较低开销 |
| 需要网络栈 | | 不需要网络栈 |
| 安全性较低 | | 安全性较高 |
+------------------+ +------------------+
8. 弹性元空间
Java 16引入了弹性元空间,更高效地将未使用的HotSpot类元数据内存(元空间)返回给操作系统。
# 配置元空间
java -XX:MetaspaceSize=64m -XX:MaxMetaspaceSize=256m -jar app.jar
+------------------+ +------------------+
| 传统元空间 | | 弹性元空间 |
+------------------+ +------------------+
| 分配大块内存 | | 分配小块内存 |
| 很少释放内存 | | 积极释放内存 |
| 内存使用效率低 | | 内存使用效率高 |
+------------------+ +------------------+
9. 启用C++14语言特性
Java 16在JDK源代码中启用了C++14语言特性,这是JDK内部实现的改进。
10. 迁移到GitHub
Java 16将JDK源代码存储库从Mercurial迁移到了GitHub。
11. 其他Java 16特性
- Alpine Linux端口:为Alpine Linux添加了JDK端口。
- Windows/AArch64端口:为Windows/AArch64添加了JDK端口。
- ZGC并发线程处理:改进了ZGC的并发线程处理。
- 禁用偏向锁:默认禁用偏向锁。
总结
Java 13到Java 16引入了许多重要的新特性和改进,包括:
Java 13主要特性:
- 文本块(预览)
- Switch表达式更新(预览)
- 重新实现Socket API
- ZGC:取消提交未使用内存
- 动态CDS归档
Java 14主要特性:
- Switch表达式(标准化)
- 记录类(预览)
- 模式匹配for instanceof(预览)
- 有用的NullPointerException
- 打包工具(孵化器)
- 非易失性映射字节缓冲区
Java 15主要特性:
- 文本块(标准化)
- 密封类(预览)
- 隐藏类
- 记录类和模式匹配的第二次预览
- 爱德华兹曲线算法
- ZGC生产就绪
- Shenandoah GC生产就绪
Java 16主要特性:
- 记录类(标准化)
- 模式匹配for instanceof(标准化)
- 密封类(第二次预览)
- 向量API(孵化器)
- 外部存储器访问API(孵化器)
- 打包工具(孵化器)
- Unix域套接字
- 弹性元空间
这些新特性使Java更加现代化、简洁和高效,为开发者提供了更多的工具和选择。
+------------------+ +------------------+ +------------------+ +------------------+
| Java 13特性 | | Java 14特性 | | Java 15特性 | | Java 16特性 |
+------------------+ +------------------+ +------------------+ +------------------+
| 文本块(预览) | | Switch表达式 | | 文本块 | | 记录类 |
| Switch表达式更新 | | 记录类(预览) | | 密封类(预览) | | 模式匹配instanceof|
| 重新实现Socket | | 模式匹配instanceof| | 隐藏类 | | 密封类(第二次预览)|
| ZGC改进 | | 有用的NPE | | 爱德华兹曲线算法 | | 向量API(孵化器) |
| 动态CDS归档 | | 打包工具(孵化器) | | ZGC生产就绪 | | 外部存储器访问API|
| | | 非易失性映射 | | Shenandoah生产 | | 打包工具(孵化器) |
| | | 字节缓冲区 | | 就绪 | | Unix域套接字 |
| | | | | | | 弹性元空间 |
+------------------+ +------------------+ +------------------+ +------------------+
练习
- 使用文本块创建HTML、JSON或SQL字符串。
- 使用Switch表达式简化代码。
- 创建并使用记录类表示不可变数据。
- 使用instanceof的模式匹配简化类型检查和转换。
- 尝试使用密封类控制继承层次结构。