Java 各版本新特性

JEP Index 提案索引 openjdk-projects https://docs.oracle.com/en/java/javase/index.html Java新特性

JDK 8 LTS

Stream API

Java Stream API

内存区域实现/调整

内存区域图

方法区(Method Area)

方法区(Method Area)与Java堆一样, 是各个线程共享的内存区域, 它用于存储已被虚拟机加载的类信息, 常量, 静态变量, 即时编译器编译后的代码等数据; 虽然Java虚拟机规范把方法区描述为堆的一个逻辑部分, 但是它却有一个别名叫做Non-Heap(非堆), 目的应该是与Java堆区分开来;

很多人都更愿意把方法区称为”永久代”(Permanent Generation); 从jdk1.7已经开始准备”去永久代”的规划, jdk1.7的HotSpot中, 已经把原本放在方法区中的静态变量, 字符串常量池等移到堆内存中;

在jdk1.8中, 永久代已经不存在, 存储的类信息, 编译后的代码数据等已经移动到了元空间(MetaSpace) 中,元空间并没有处于堆内存上, 而是直接占用的本地内存(NativeMemory);

元空间的本质和永久代类似, 都是对JVM规范中方法区的实现; 不过元空间与永久代之间最大的区别在于: 元空间并不在虚拟机中, 而是使用本地内存; 因此, 默认情况下, 元空间的大小仅受本地内存限制, 但可以通过以下参数来指定元空间的大小:

-XX:MetaspaceSize, 初始空间大小, 达到该值就会触发垃圾收集进行类型卸载, 同时GC会对该值进行调整: 如果释放了大量的空间, 就适当降低该值; 如果释放了很少的空间, 那么在不超过MaxMetaspaceSize时, 适当提高该值;
-XX:MaxMetaspaceSize, 最大空间, 默认是没有限制的;

除了上面两个指定大小的选项以外, 还有两个与 GC 相关的属性: -XX:MinMetaspaceFreeRatio, 在GC之后, 最小的Metaspace剩余空间容量的百分比, 减少为分配空间所导致的垃圾收集 -XX:MaxMetaspaceFreeRatio, 在GC之后, 最大的Metaspace剩余空间容量的百分比, 减少为释放空间所导致的垃圾收集

如果不设置JVM将会根据一定的策略自动增加本地元内存空间; 如果你设置的元空间内存过小, 你的应用程序可能得到以下错误: java.lang.OutOfMemoryError: Metadata space

在Java7之前, HotSpot虚拟机中将GC分代收集扩展到了方法区, 使用永久代来实现了方法区; 这个区域的内存回收目标主要是针对常量池的回收和对类型的卸载; 而在Java8中, 已经彻底没有了永久代, 将方法区直接放在一个与堆不相连的本地内存区域, 这个区域被叫做元空间;

JDK 9

jdk 9 新特性

模块化 提供了List.of(), Set.of(), Map.of()和Map.ofEntries()等工厂方法 接口支持私有方法 Optional 类改进 多版本兼容Jar包 JShell工具 try-with-resources的改进 Stream API的改进

JDK9以上模块不能使用反射去访问非公有的成员/成员方法以及构造方法, 下参数运行允许所有反射 --illegal-access=deny --add-opens java.base/java.lang=ALL-UNNAMED

///一些jdk 的模块 以及包

--add-opens=java.base/java.lang=ALL-UNNAMED
--add-opens=java.base/java.util=ALL-UNNAMED
--add-opens=java.base/jdk.internal.misc=ALL-UNNAMED
--add-opens=java.base/jdk.internal.loader=ALL-UNNAMED
--add-opens=java.desktop/java.awt.event=ALL-UNNAMED
--add-opens=java.desktop/sun.font=ALL-UNNAMED
--add-opens=java.desktop/java.awt=ALL-UNNAMED
--add-opens=java.desktop/sun.awt=ALL-UNNAMED
--add-opens=java.desktop/javax.swing=ALL-UNNAMED
--add-opens=java.desktop/sun.swing=ALL-UNNAMED
--add-opens=java.desktop/sun.lwawt.macosx=ALL-UNNAMED
--add-opens=java.desktop/sun.lwawt=ALL-UNNAMED
--add-opens=java.desktop/javax.swing.plaf.basic=ALL-UNNAMED
--add-opens=java.desktop/java.awt.peer=ALL-UNNAMED
--add-opens=java.desktop/javax.swing.text.html=ALL-UNNAMED
--add-opens=java.desktop/sun.awt.windows=ALL-UNNAMED
--add-opens=java.desktop/sun.awt.image=ALL-UNNAMED
--add-opens=java.desktop/sun.java2d=ALL-UNNAMED

--add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED
--add-opens=jdk.jdi/com.sun.tools.javac.code=ALL-UNNAMED
--add-opens=jdk.jdi/com.sun.tools.jdi=ALL-UNNAMED
--add-opens=java.base/com.sun.tools.jdi=ALL-UNNAMED

模块化 JEP 201

JEP 201: Modular Source Code

Java 9 Module System在Early Access JDK中具有95个模块; Oracle Corp已将JDK jar和Java SE规范分为两组模块;

所有JDK模块均以” jdk.”开头 所有Java SE规范模块均以” java.”开头

模块声明

一个模块的自描述表现在它的模块声明中, 它是java程序语言中的一个新的结构, 最简单的可能的模块声明仅仅是指定模块的名字

声明 模块名称 module com.foo.bar { }

声明 依赖其他模块

module com.foo.bar {
    requires org.baz.qux;
}

声明 其他模块可以访问的数据 最后, exports 项可以被添加, 它可以仅仅使指定包(package)中的公共类型可以被其他的模块使用;

module com.foo.bar {
    requires org.baz.qux;
    exports com.foo.bar.alpha;
    exports com.foo.bar.beta;
}

如果一个模块的声明中没有 exports 项, 则它根本不向其他模块输出任何的类型;

快速创建集合 JEP 269

JEP 269: Convenience Factory Methods for Collections

集合, Stream 和 Optional 增加 了 List.of(), Set.of(), Map.of()Map.ofEntries()等工厂方法来创建不可变集合

Set<String> nameSet = Set.of("Justin", "Monica");
Map<String, Integer> scoreMap = Map.of("Justin", 95, "Monica", 100);
List<String> nameLt = List.of("Justin", "Monica");
 

Stream 中增加了新的方法 ofNullable, dropWhile, takeWhile 和 iterate 方法; Collectors 中增加了新的方法 filtering 和 flatMapping

改进 Optional

java.util.Optional 添加了很多新的有用方法, 新增了 ifPresentOrElse, or 和 stream 等方法

轻量级的 JSON API

轻量级的 JSON API: 内置了一个轻量级的 JSON API

私有接口方法 在接口中使用private私有方法; 我们可以使用 private 访问修饰符在接口中编写私有方法;

进程 API 改进的 API 来控制和管理操作系统进程; 引进 java.lang.ProcessHandle 及其嵌套接口 Info 来让开发者逃离时常因为要获取一个本地进程的 PID 而不得不使用本地代码的窘境;

try-with-resource

JEP 213: Milling Project Coin

作为 Milling Project Coin 的一部分,try-with-resources 声明在 JDK 9 已得到改进。如果你已经有一个资源是 final 或等效于 final 变量,您可以在 try-with-resources 语句中使用该变量,而无需在 try-with-resources 语句中声明一个新变量。

任何继承了Closeable或者AutoCloseable的类(Closeable接口继承自AutoCloseable接口),都可以放在try-with-resource里使用,以便在try块结束的时候自动释放资源close;

JShell JEP 222

自带一个 jshell 工具, 可以实时执行java 代码的控制台 222: jshell: The Java Shell (Read-Eval-Print Loop)

JDK 10

var 关键字 list, set, map 提供了静态方法copyOf()返回入参集合的一个不可变拷贝 Optional 新增了orElseThrow()方法来在没有值时抛出异常

JDK 11 LTS

增加一些字符串方法 strip()方法,和trim()相比,strip()可以去掉Unicode空格 开始允许开发者在 Lambda 表达式中使用 var 进行参数声明 Http Client重写, 支持HTTP/1.1和HTTP/2 , 也支持 websockets 对Files类增加了writeString和readString两个静态方法,可以直接把String写入文件,或者把整个文件读出为一个String 可运行单一Java源码文件, 如: java Test.java ZGC: 可伸缩低延迟垃圾收集器 支持 TLS 1.3 协议

Features

181: Nest-Based Access Control
309: Dynamic Class-File Constants
315: Improve Aarch64 Intrinsics
318: Epsilon: A No-Op Garbage Collector
320: Remove the Java EE and CORBA Modules
321: HTTP Client (Standard)
323: Local-Variable Syntax for Lambda Parameters
324: Key Agreement with Curve25519 and Curve448
327: Unicode 10
328: Flight Recorder
329: ChaCha20 and Poly1305 Cryptographic Algorithms
330: Launch Single-File Source-Code Programs
331: Low-Overhead Heap Profiling
332: Transport Layer Security (TLS) 1.3
333: ZGC: A Scalable Low-Latency Garbage Collector
   (Experimental)

335: Deprecate the Nashorn JavaScript Engine
336: Deprecate the Pack200 Tools and API

JDK 12

Switch 表达式扩展 Switch Expressions (Preview) 新增NumberFormat对复杂数字的格式化 字符串支持transform, indent操作 新增方法Files.mismatch(Path, Path) Teeing Collector 支持unicode 11

JDK 13

Switch 表达式扩展(引入 yield 关键字) 文本块(预览功能) SocketAPI 重构 FileSystems.newFileSystem新方法 增强 ZGC 释放未使用内存

JDK 14

instanceof 增强(预览) Record 类型, 类似于Lombok 的@Data注解 Switch 表达式-标准化(增强转正) 改进 NullPointerExceptions提示信息(空指针异常精准提示) 删除 CMS 垃圾回收器

Record

开发人员想要创建纯数据载体类(plain data carriers)通常都必须编写大量低价值, 重复的, 容易出错的代码; 如: 构造函数, getter/setter, equals(), hashCode()以及toString()等; Record 就是解决此类痛点

  1. Java 编译器将为 record 类型生成 equals(), hashCode(), toString() 方法, 以及生成适当的构造函数, 并且为所有字段生成 getter 和 setter
  2. 定义class时使用final, 无法派生子类;
  3. 每个字段使用final, 保证创建实例后无法修改任何字段;
record Point(int x, int y) { } 
 

等同于:

public final class Point {
    private final int x;
    private final int y;
 
    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }
 
    public int x() {
        return this.x;
    }
 
    public int y() {
        return this.y;
    }
 
    public boolean equals(Object obj) {
        // implement equals based on fields x and y
    }
 
    public int hashCode() {
        // implement hashCode based on fields x and y
    }
 
    public String toString() {
        // implement toString based on fields x and y
    }
}

JDK 15

EdDSA 数字签名算法 移除 Nashorn JavaScript 引擎 改进java.net.DatagramSocket 和 java.net.MulticastSocket底层实现 Java SE 15(即将于 2020 年 9 月发布)引入密封类作为预览特性 (见 JDK 17)

文本块 Text Blocks

JEP 378: Text Blocks

文本块 是JDK15正式支持的!

String html = """
    <html>
    <body>
    <input type="number"/>
    </body>
    </html>
    """;

Hidden Classes(隐藏类)

public class JEP371HiddenClasses {
    public static String hello() {
        return "https://www.didispace.com";
    }
 
}

javac 编译一下

String filePath = "JEP371HiddenClasses.class";
byte[] b = Files.readAllBytes(Paths.get(filePath));
String base64 = Base64.getEncoder().encodeToString(b)
 
//拿到class 的 byte[]
byte[] classInBytes = getDecoder().decode(base64);
Class<?> proxy = MethodHandles.lookup()
.defineHiddenClass(classInBytes, true, MethodHandles.Lookup.ClassOption.NESTMATE)
.lookupClass();
 
// 输出类名
log.info(proxy.getName());
// 输出类有哪些函数
for(Method method: proxy.getDeclaredMethods()) {
log.info(method.getName());
}
 
// 调用hello函数
MethodHandle mh = MethodHandles.lookup().findStatic(proxy, "hello", MethodType.methodType(String.class));
String result = (String) mh.invokeExact();
log.info(result);
 

JDK 16

内存管理的提升 新的打包工具(jpackage 的工具, 用于独立打包 Java 应用程序) UNIX-Domain Socket channels Value-based Classes的警告 Encapsulating JDK Internals by default 提供了 C++ 14语言特性

instanceof 增强

//旧的写法
if (value instanceof String) {
  String s = (String) value;
  System.out.println(s.substring(1));
}
 
//新的增强写法
if (value instanceof String s) {
  System.out.println(s.substring(1));
}

jpackage

jpackage 的工具, 用于独立打包 Java 应用程序; jpackage 在 JDK 14 中被作为孵化工具引入, 并在 JDK 15 中仍处于孵化阶段;

到了JDK 16, jpackage 将投入生产, 支持本地的软件包格式, 从而为用户提供自然的安装体验, 并允许在打包时指定启动时参数; 支持的格式包括 Windows 上的 msi 和 exe , MacOS 上的 pkg 和 dmg 以及 Linux 上的 deb 和 rpm ;

该工具可以直接从命令行或以编程方式调用; 新的打包工具解决了这样一种情况: 许多Java应用程序需要以全局可用的方式安装在本机平台上, 而不是简单地放置在类路径或模块路径上; 因此提供适合本机平台的可安装软件包非常有必要;

JDK 17 LTS

https://openjdk.org/projects/jdk/17/

Features

密封类 Sealed Classes

Sealed Classes

  1. 可以用来声明类或接口, 对哪些类或接口可以扩展(继承/实现)它们进行了限制
  2. 方便编译器可以控制穷举类型
  3. 限制可访问性和可扩展性, 防止滥用继承

例如, 定义一个Shape类:

public sealed class Shape permits Rect, Circle, Triangle {
    ...
}

上述Shape类就是一个sealed 类, 它只允许指定的 Rect, Circle, Triangle 3个类继承它;

public final class Rect extends Shape {...}

注意这里 Rect 类, 因为父类Shape 被sealed修饰之后, sealed 的密封要求被传递过来, 此时子类就必须在sealed, non-sealed, final之间选择一个定义

sealed: 修饰类/接口, 用来描述这个类/接口为密封类/接口 non-sealed: 修饰类/接口, 用来描述这个类/接口为非密封类/接口 permits: 用在 extends 和 implements 之后, 指定可以继承或实现的类

JDK 18

默认字符集为 UTF-8 终于!

简单 Web 服务器 该提案提供了一个简约的 Web 服务器,它只能提供静态文件。没有提供 CGI 或类似 servlet 的功能

@snippet 增加了 @snippet标签,简化 API 文档中包含示例源代码的情况; java doc注解用的 使用语法: {@snippet …}

弃用 Finalization 现在使用 finalize 方法, 已经标识为废弃状态了; jdk9开始就标为弃用,强调一下?

JDK 19

虚拟线程 (预览), 线程状态增加了很多, 可以在执行阻塞时, 让出资源 外部函数和内存 API (预览), 外部函数和内存 API 曾在 JDK 17 中孵化,而后在 JDK 18 中重新孵化。

虚拟线程 Virtual Threads (Preview)

Virtual Threads (Preview)|

Java中的线程是对操作系统线程(操作系统线程是重量级, 撑死10万就要崩了)的一个简单包装,线程的创建,调度和销毁等都是由操作系统完成; 线程切换需要消耗CPU时间,这部分时间是与业务无关的;线程的性能直接受操作系统处理能力的影响; 因此, Java 中的线程是一种重量级的资源;

JDK19当中的虚拟线程,他们则是用户模式线程。即由应用程序自己实现和管理的,而不是由操作系统内核实现和管理。

相关术语

  • 操作系统线程(OS Thread):由操作系统管理的“类似线程”的数据结构。
  • 平台线程(Platform Thread):在虚线程概念出来前,java.Lang.Thread类的每个实例,都是一个平台线程,是操作系统线程的包装。
  • 虚拟线程(Virtual Thread):一种轻量级,由JVM管理的线程。对应java.lang.VirtualThread这个类。
  • 载体线程(Carrier Thread):一个命名约定,Java中不存在相应的类,是虚拟线程的一个载体线程。一个虚拟线程倘若装载到一个平台线程之后,那么这个平台线程就可以称之为虚拟线程的一个载体线程。

创建平台线程,就回创建操作系统线程。若阻塞平台线程,同样会阻塞操作系统线程。虚拟线程不能直接执行,需要装载(Mount)到平台线程,依靠平台线程执行任务。倘若虚拟线程发生了阻塞,会从平台线程上卸载(Unount),达到不影响平台线程的目的。

如何卸载?

直接调用 Thread::sleep 即可, 这会让出平台线程的时间 , (在 Java19 中 sleep 方法被重写了,重写后的方法里还增加了虚拟线程相关的判断)

Thread.sleep(10);

调度方式

  • 平台线程 基于操作系统线程实现的平台线程,JDK 依赖于操作系统中的线程调度程序来进行调度

  • 虚拟线程 对于虚拟线程,JDK 有自己的调度器。JDK 的调度器没有直接将虚拟线程分配给系统线程,而是将虚拟线程分配给平台线程

in short 虚拟线程需要挂载到’平台线程’运行, 但它不能阻塞’平台线程’会被卸载, 等类似的调度方式有JVM自己调度,跟操作系统平台无关

虚拟线程 创建

通过 Thread.startVirtualThread() 创建

    Thread.startVirtualThread(() -> System.out.println("hello"));

通过 Thread.ofVirtual() 创建

// 创建一个虚拟线程,但是不启动
Thread unStarted = Thread.ofVirtual().unstarted(() -> System.out.println("hello"));
unStarted.start();
// 创建一个虚拟线程并启动
Thread.ofVirtual().start(() -> System.out.println("hello2"));

通过 ThreadFactory 创建

// 创建一个虚拟线程,但是不启动
ThreadFactory factory = Thread.ofVirtual().factory();
Thread t = factory.newThread(() -> System.out.println("hello"));
t.start();

通过 newVirtualThreadPerTaskExecutor() 创建

ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor(); executor.submit(() -> System.out.println("hello"));

虚拟线程 同步

在同步块或方法内执行 synchronized 阻塞操作会导致 JDK 的虚拟线程调度程序阻塞宝贵的操作系统线程,

 
// 避免长时间和频繁的 synchronized 优先使用 ReentrantLock
ReentrantLock lock = new ReentrantLock();
 
 
 

JDK 20

This release is the Reference Implementation of version 20 of the Java SE Platform, as specified by JSR 395 in the Java Community Process.

JDK 20 reached General Availability on 21 March 2023. Production-ready binaries under the GPL are available from Oracle; binaries from other vendors will follow shortly.

The features and schedule of this release were proposed and tracked via the JEP Process, as amended by the JEP 2.0 proposal. The release was produced using the JDK Release Process (JEP 3).

Features 429: Scoped Values (Incubator) 432: Record Patterns (Second Preview) 433: Pattern Matching for switch (Fourth Preview) 434: Foreign Function & Memory API (Second Preview) 436: Virtual Threads (Second Preview) 437: Structured Concurrency (Second Incubator) 438: Vector API (Fifth Incubator)

JDK 21

Features

Schedule

2023/06/08Rampdown Phase One (fork from main line)
2023/07/20Rampdown Phase Two
2023/08/10Initial Release Candidate
2023/08/24Final Release Candidate
2023/09/19General Availability

JDK22

https://docs.oracle.com/en/java/javase/22/index.html Java Project Panama

JDK25 LTS

https://blogs.oracle.com/java/post/the-arrival-of-java-25

JEP - https://openjdk.org/projects/jdk/25/