Java9至17的新特性总结
总览
讲讲Java 9-17 的一些语法糖和一些新发布的jeps, 重点讲讲JVM的垃圾回收器
时间线
SpringBoot 为什么选择Java17这个版本。我估计跟下面这个图有关系。
Java 8 新特性
这里简单罗列一下Java 8 发布的jeps
1、Lambda表达式
2、函数式编程
3、接口可以添加默认方法和静态方法,也就是定义不需要实现类实现的方法
4、方法引用
5、重复注解,同一个注解可以使用多次
6、引入Optional来避免空指针
7、引入Streams相关的API
8、引入新的Date/Time相关的API
9、新增jdeps命令行,来分析类、目录、jar包的类依赖层级关系
10、JVM使用MetaSpace代替了永久代(PermGen Space)
Java 9 新特性
jeps
102:进程 API 更新
110:HTTP 2 客户端 *
143:改进竞争锁定
158:统一 JVM 日志记录
165:编译器控制
193:变量句柄
197:分段代码缓存
199:智能 Java 编译,第二阶段
200:模块化 JDK *
201:模块化源代码 *
211:在导入语句中省略弃用警告
212:解决 Lint 和 Doclint 警告
213:Milling Project Coin
214:删除 JDK 8 中已弃用的 GC 组合 * https://openjdk.java.net/jeps/173
215:javac 的分层归因
216:正确处理导入语句
217:注释管道 2.0
219:数据报传输层安全性 (DTLS)
220:模块化运行时图像
221:简化的 Doclet API
222:jshell:Java Shell(读取-评估-打印循环)
223:新版本字符串方案
224:HTML5 Javadoc
225:Javadoc 搜索
226:UTF-8 属性文件
227:Unicode 7.0
228:添加更多诊断命令
229:默认创建 PKCS12 密钥库
231:删除启动时 JRE 版本选择
232:提高安全应用程序性能
233:自动生成运行时编译器测试
235:测试由 javac 生成的类文件属性
236:Nashorn 解析器 API
237:Linux/AArch64 端口
238:多版本 JAR 文件
240:删除 JVM TI hprof 代理
241:删除 jhat 工具
243:Java 级 JVM 编译器接口
244:TLS 应用层协议协商扩展
245:验证 JVM 命令行标志参数
246:利用 GHASH 和 RSA 的 CPU 指令
247:为旧平台版本编译
248:使 G1 成为默认垃圾收集器 ***
249:用于 TLS 的 OCSP 装订
250:在 CDS 档案中存储内部字符串
251:多分辨率图像
252:默认使用 CLDR 区域设置数据
253:为模块化准备 JavaFX UI 控件和 CSS API
254:紧凑字符串
255 :将选定的 Xerces 2.11.0 更新合并到 JAXP
256: BeanInfo 注释
257:将 JavaFX/Media 更新到 GStreamer 的较新版本
258: HarfBuzz 字体布局引擎
259: Stack-Walking API
260:封装大多数内部 API
261:模块系统 **
262:TIFF 图像 I/O
263:Windows 和 Linux 上的 HiDPI 图形
264:平台日志记录 API 和服务
265:Marlin 图形渲染器
266:更多并发更新 **
267:Unicode 8.0
268:XML 目录
269:集合的便利工厂方法 **
270:保留关键部分的堆栈区域
271:统一 GC 日志记录
272:特定于平台的桌面功能
273:基于 DRBG 的 SecureRandom 实现
274:增强的方法句柄
275:模块化 Java 应用程序打包 **
276:语言定义的对象模型的动态链接
277:增强的弃用
278:G1 中巨大对象的附加测试
279:改进测试失败故障排除
280:指示字符串连接
281:HotSpot C++ 单元测试框架
282:jlink:Java 链接器
283:在 Linux 上启用 GTK 3
284:新的 HotSpot 构建系统
285:Spin-Wait 提示
287:SHA-3 哈希算法
288:禁用 SHA-1 证书
289:弃用 Applet API
290:过滤传入的序列化数据
291:弃用并发标记清除 (CMS) 垃圾收集器
292:在 Nashorn 中实现选定的 ECMAScript 6 功能
294:Linux/s390x 端口
295:提前编译
297:统一 arm32/arm64 端口
298:删除演示和示例
299:重新组织文档
集合的便利工厂方法
Map<String, String> of = Map.of();
Map<String, Integer> e = Map.of("e", 1);
Map<String, Integer> e1 = Map.ofEntries(Map.entry("e", 1));
Set<String> set = Set.of("e");
List<App> list = List.of();
响应式编程
Flow API 是 Java 9 引入的响应式编程的接口 RXJava
java.util.concurrent.Flow;
Publisher:发布者,负责发布消息 定义了生产数据和控制事件的方法;
Subscriber:订阅者,负责订阅处理消息 定义了消费数据和事件的方法 ;
Subscription:订阅控制类,可用于发布者和订阅者之间通信 定义了链接Publisher和Subscriber的方法;
Processor:处理者,同时充当Publisher和Subscriber的角色 定义了转换Publisher到Subscriber的方法
class SubmissionPublisher<T>是Flow.Publisher<T>的实现,她可以灵活的生产数据,同时与Reactive Stream兼容。
SubmissionPublisher<String> publisher = new SubmissionPublisher<>();
publisher.consume(v -> System.out.println("consumer:" + v));
publisher.submit("1");
publisher.submit("2");
publisher.submit("3");
publisher.submit("4");
publisher.offer("3",(d,v)->{
System.out.println("droped");
return true;
});
新的HTTP API
JDK 9 已经完成了一些原型设计工作,于JDK10发布
设计目标
必须易于用于常见情况,包括简单的阻塞模式。
必须提供事件通知,例如“收到的标头”、错误和“收到的响应正文”。此通知不一定基于回调,但可以使用 CompletableFuture 等异步机制。
一个简单而简洁的 API,可满足 80-90% 的应用程序需求。这可能意味着相对较小的 API 占用空间,不一定会暴露协议的所有功能。
必须向服务器公开 HTTP 协议请求的所有相关方面,以及来自服务器的响应(标头、正文、状态代码等)。
必须支持标准和通用的身份验证机制。这最初将仅限于基本身份验证。
必须能够轻松设置 WebSocket 握手。
必须支持HTTP/2。(HTTP/2 的应用程序级语义与 1.1 基本相同,尽管有线协议完全不同。)
必须能够协商从 1.1 升级到 2(或不升级),或者从一开始就选择 2。
必须支持服务器推送,即服务器无需客户端明确请求即可向客户端推送资源的能力。
必须执行与现有网络 API 一致的安全检查。
应该对 lambda 表达式等新的语言特性友好。
应该对嵌入式系统要求友好,特别是避免永久运行的计时器线程。
必须支持 HTTPS/TLS。
HTTP/1.1 的性能要求:
性能必须与现有HttpURLConnection 实施相提并论。
当用作客户端 API 时,性能必须与 Apache HttpClient 库以及 Netty 和 Jetty 相当。
新 API 的内存消耗必须与HttpURLConnectionApache HttpClient 以及 Netty 和 Jetty 用作客户端 API 的内存消耗相当或更低。
HTTP/2 的性能要求:
尽管有任何平台限制(例如,TCP 段确认窗口),性能必须在新协议所期望的方面(即在可扩展性和延迟方面)优于 HTTP/1.1。
当用作 HTTP/2 的客户端 API 时,性能必须与 Netty 和 Jetty 相当。
HttpURLConnection新 API 的内存消耗必须与使用、Apache HttpClient 以及用作客户端 API 时的 Netty 和 Jetty相同或更低。
性能比较只会在可比较的操作模式的上下文中进行,因为新的 API 将强调简单性和易用性,而不是涵盖所有可能的用例,
这项工作是为 JDK 9 设计的。Java EE 在 Servlet 4.0 API 中的 HTTP/2 实现中可能会重用一些代码,
因此仅使用 JDK 8 语言功能,并在可能的情况下使用 API。
其目的是利用在 JDK 9 中使用 API 的经验,可以在 JDK 10 中的 java.net 命名空间下标准化 Java SE 中的 API。
当这种情况发生时,作为未来 JEP 的一部分,API将不再作为孵化器模块存在。
模块化
JDK模块化
应用程序模块化
通过定义module-info.java来控制Java的模块化
open module feature { //open 开放整个模块 为了运行时的反射
requires xxx; // 需要使用该模块
requires transitive xxx; //继承xxx的隐式可读
requires static jdk.internal.vm.ci; //编译期检查该模块是否已存在
exports xxxx;
exports xxx to xxx; //限制导出 to 后面的模块才能访问他
//服务发现 ServiceLoader.load(interface.class);
provides Interface with Impl; //给interface 提供一个服务Impl
uses Interface;//使用该interface 通过ServiceLoader 无需配置META-INFO
opens jdk9.http2; //其他模块可以进行深度反射
opens jdk9 to java.base; //只能由该模块可以进行深度反射
}
vm 参数
--illegal-access=permit //运行反射
--add-opens
类加载器
JDK9后
- 引导类加载器:定义核心Java SE和JDK模块。
- 平台类加载器:定义部分Java SE和JDK模块。
- 应用或系统类加载器:定义CLASSPATH上的类和模块路径中的模块。
JDK9之前
文件流
var classLoader = ClassLoader.getSystemClassLoader();
InputStream in = classLoader.getResourceAsStream("file");
try (var os = new FileOutputStream("file2")){
in.transferTo(os);
}
in.close();
Java 10 新特性
jeps
286:本地变量类型推断 *
296:将 JDK 整合到一个存储库中
304:垃圾收集器接口
307:G1 的并行Full GC *
310:应用程序类数据共享
312:线程本地握手
313:删除 Native-Header生成工具 (javah)
314:附加 Unicode 语言标签扩展
316:替代存储设备上的堆分配
317:基于 Java 的实验性 JIT 编译器 Graal *
319:根证书
322:基于时间的发布版本控制
新语法糖
var ss ="111";
var list = List.of();
var map = Map.of();
var list2 = List.copyOf(list);
var app = new VarApp();
var set = Set.of();
for (var i = 0; i <10; i++) {
System.out.println(i);
}
System.out.println(int.class.getName());
Stream<Integer> integerStream = Stream.of(3, 5, 6, 4, 5);
LocalDate of = LocalDate.of(2022, 2, 28);
//java9
// 遇到 不满足条件就跳出循环
System.out.println("遇到 不满足条件就跳出循环》》》》》》》》》》》》");
integerStream.takeWhile(t->t%2!=0).forEach(System.out::println);
System.out.println("只要碰到满足的就把后面的数据留下来");
integerStream = Stream.of(3, 4, 6, 4, 5,3);
integerStream.dropWhile(t->t%2!=0).forEach(System.out::println);
//流迭代
Stream.iterate(2,n->n+1).limit(10).forEach(System.out::println);
Stream.iterate(2,t->t<100,n->n+1).forEach(System.out::println);
GraalVM
GraalVM想成为一统天下的“最终”虚拟机
Java 11 新特性
jeps
181:基于嵌套的访问控制
309:动态类文件常量
315:改进 Aarch64 内在函数
318:Epsilon:无操作垃圾收集器
320:删除 Java EE 和 CORBA 模块
321:HTTP 客户端(标准) *
323:var 变量Lambda 参数的语法 *
324:与 Curve25519 和 Curve448 的密钥协议
327:Unicode 10
328:JFR JVM监控 ***
329:ChaCha20 和 Poly1305 加密算法
330:启动单文件源代码程序
331:低开销堆分析
332:传输层安全 (TLS) 1.3
333:ZGC:可扩展的低延迟垃圾收集器(实验性) *
335:弃用 Nashorn JavaScript 引擎
336:弃用 Pack200 工具和 API
HTTP API
HTTP 客户端是在 Java 11 中添加的。它可用于通过网络请求 HTTP 资源。
它支持 同步和异步编程模型的HTTP/1.1和HTTP/2,将请求和响应主体作为响应流处理,并遵循熟悉的构建器模式。
HttpClient client = HttpClient.newHttpClient();
client = HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_2)
.followRedirects(HttpClient.Redirect.NORMAL)
.proxy(ProxySelector.of(new InetSocketAddress("www-proxy.com", 8080)))
.authenticator(Authenticator.getDefault())
.build();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://baidu.com/"))
.build();
request = HttpRequest.newBuilder()
.uri(URI.create("http://openjdk.java.net/"))
.timeout(Duration.ofMinutes(1))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString("{name:\"wang\",age:18}"))
.build();
HttpResponse<String> response =
client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.statusCode());
System.out.println(response.body());
client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
.thenApply(HttpResponse::body)
.thenAccept(System.out::println)
.join();
JFR
原生jvm事件流监控
Configuration config = Configuration.getConfiguration("default");
try (var es = new RecordingStream(config)) {
es.onEvent("jdk.GarbageCollection", System.out::println);
es.onEvent("jdk.CPULoad", System.out::println);
es.onEvent("jdk.JVMInformation", System.out::println);
es.setMaxAge(Duration.ofSeconds(10));
es.start();
}
Optional
Optional<String> s = Optional.ofNullable(null);
//or stream orElseThrow isEmpty ifPresentOrElse
String c = s.or(() -> Optional.of("c")).get();
System.out.println(c);
Order order = new Order();
Optional<Store> storeOptional = Optional.ofNullable(null);
storeOptional.ifPresentOrElse(k->{
order.storeName=k.name;
},
()->{
order.storeName ="未知";
}
);
String
System.out.println("".isBlank());
// 取前后的空格
System.out.println(" java ".strip());
// 取前的空格
System.out.println(" java".stripLeading());
// 取后的空格
System.out.println("java ".stripTrailing());
//复制字符串
System.out.println("java".repeat(2));
//获取行数
System.out.println("java\n java\n java\n".lines().count());
Java 12 新特性
jeps
189: Shenandoah:一种低暂停时间的垃圾收集器(实验性)
230: 微基准套件
325: swtich表达式(预览)
334: JVM 常量 API
340: 一个 AArch64 端口,不是两个
341: 默认 CDS 档案
344: G1 的可中止混合集合
346: 立即从 G1 返回未使用的已提交内存
语法糖
public static void main(String[] args) {
int day = 1;
Integer integer = get(day);
System.out.println(integer);
double mean = Stream.of(1, 2, 3, 4, 5).collect(Collectors.teeing(
//总和
Collectors.summingDouble(i -> i),
//元素数量
Collectors.counting(),
//处理结果
(sum, n) -> sum / n
));
String ms = "app";
String transform = ms.transform(t -> t + "aa");
System.out.println(transform);
}
public static int get(int day) {
return switch (day) {
case MONDAY, FRIDAY, SUNDAY -> 6;
case TUESDAY -> 7;
case THURSDAY, SATURDAY -> 8;
case WEDNESDAY -> 9;
default -> 0;
};
}
Java 13 新特性
JEPS
350: 动态 CDS 档案
351: ZGC:取消提交未使用的内存
353: 重新实现 Legacy Socket API
354: 切换表达式(预览)
355: 文本块(预览)
文本块
String html1=" <html>\n" +
" <body>\n" +
" <p>Hello, world</p>\n" +
" </body>\n" +
" </html>";
System.out.println(html1);
String html =
"""
<html>
<body>
<p>Hello, world</p>
</body>
</html>
""";
System.out.println(html);
Java 14 新特性
JEPS
305: instanceof 的模式匹配(预览版)
343: 包装工具(孵化器)
345: G1 的 NUMA 感知内存分配
349: JFR 事件流
352: 非易失性映射字节缓冲区
358: 有用的 NullPointerExceptions
359: record(预览)
361: Switch表达式(标准)
362: 弃用 Solaris 和 SPARC 端口
363: 删除并发标记清除 (CMS) 垃圾收集器
364: macOS 上的 ZGC
365: Windows 上的 ZGC
366: 弃用 ParallelScavenge + SerialOld GC 组合
367: 删除 Pack200 工具和 API
368: 文本块(第二次预览)
370: Foreign-Memory Access API(孵化器)
record
record Person<T>(String name,Integer age){
public List<T> getList(){
return List.of();
}
}
instanceof
public static void getList(Object o) {
if (o instanceof String msg) {
System.out.println(msg.length());
}
}
空指针错误
B b = new Main.B();
System.out.println(b.a.c);
/**
* Cannot read field "c" because "b.a" is null
* at jdk14.Main.main(Main.java:14)
*/
Java 15 新特性
jeps
339: 爱德华兹曲线数字签名算法 (EdDSA)
360: 密封类(预览版)
371: 隐藏类
372: 移除 Nashorn JavaScript 引擎
373: 重新实现旧版 DatagramSocket API
374: 禁用和弃用偏向锁定
375: instanceof 的模式匹配(第二次预览)**
377: ZGC:可扩展的低延迟垃圾收集器 **
378: 文本块 **
379: Shenandoah:一个低暂停时间的垃圾收集器 **
381: 删除 Solaris 和 SPARC 端口
383: Foreign-Memory Access API(第二个孵化器)
384: record(第二次预览)
385: 弃用 RMI 激活以进行删除
密封类
/**
* 密封类对其允许的子类(由其permits子句指定的类)施加三个约束:
*
* 密封类及其允许的子类必须属于同一个模块,并且如果在未命名的模块中声明,则必须属于同一个包。
*
* 每个允许的子类都必须直接扩展密封类。
*
* 每个允许的子类都必须选择一个修饰符来描述它如何继续由其超类发起的密封:
*
* public 可以定制那些类可以被继承或实现
*/
public sealed abstract class Shape permits Circle,Rectangle,Square {
public non-sealed class Square extends Shape{
public class SquareQA extends Square{
public sealed class Rectangle extends Shape permits FilledRectangle {
public final class FilledRectangle extends Rectangle{
public final class Circle extends Shape{
Java 16 新特性
338: 矢量 API(孵化器)
347: 启用 C++14 语言功能
357: 从 Mercurial 迁移到 Git
369: 迁移到 GitHub
376: ZGC:并发线程堆栈处理
380: Unix 域套接字通道
386: 高山 Linux 端口
387: 弹性元空间
388: Windows/AArch64 端口
389: 外部链接器 API(孵化器)
390: 基于值的类的警告
392: 打包工具
393: Foreign-Memory Access API(第三孵化器)
394: instanceof 的模式匹配
395: record
396: 默认情况下对 JDK 内部进行强封装
397: 密封课程(第二次预览)
Java 17 新特性
306: 恢复始终严格的浮点语义
356: 增强的伪随机数生成器
382: 新的 macOS 渲染管道
391: macOS/AArch64 端口
398: 弃用 Applet API 以进行删除
403: 强烈封装 JDK 内部
406: 开关的模式匹配(预览版)
407: 删除 RMI 激活
409: 密封类
410: 删除实验性 AOT 和 JIT 编译器 Graal
411: 弃用安全管理器以进行删除
412: 外部函数和内存 API(孵化器)
414: Vector API(第二个孵化器)
415: 特定于上下文的反序列化过滤器
Java 9 模块化
概念
- 模块化(modularization)是指将系统分解成独立且相互链接的木刻的行为
- 模块(module)是包含代码的可识别工件, 使用了元数据来描述模块及其与其他模块的关系
为什么引入模块化
- 减少环境资源开销,jlink打包后会打出最小镜像
- 可配置的封装隔离机制 通过module-info
模块的核心原则
- 强封装性
- 一个模块必须能够对其他模块隐藏其部分代码。这样一来,就可以在可公开使用的代码和被视为内部实现细节的代码之间划定一条清晰的界限,从而防止模块之间发生意外或不必要的耦合,即无法使用被封装的内容。
- 定义良好接口
- 虽然封装是很好的做法,但如果模块需要一起工作,那么就不能将所有的内容都进行封装。从定义上讲,没有封装的代码是模块公共API的一部分。由于其他模块可以使用这些公共代码,因此必须非常小心地管理它们。未封装代码中任何一个重大更改都可能会破坏依赖该代码的其他模块。因此,模块应该向其他模块公开定义良好且稳定的接口。
- 显式依赖
- 一个模块通常需要使用其他模块来完成自己的工作,这些依赖关系必须是模块定义的一部分,以便使模块能够独立运行。
截止到Jdk17的GC的垃圾回收器的发展史
//todo