Java9-17新特性

news/2024/12/28 16:54:33/

文章目录

  • 一、简介
  • 二、新特性
    • 接口私有方法(JDK9)
    • String存储结构的变化(JDK9)
    • 快速创建只读集合(JDK9、10)
    • 文本块(JDK13、14、15)
    • 更直观的 NullPointerException 提示(JDK14)
    • 密封类(JDK15、16、17)
    • instanceof的模式匹配(JDK14、15、16)
    • switch增强(JDK12、13、17)
    • 垃圾收回相关优化(JDK9-17)
      • JDK9
      • JDK10
      • JDK11
      • JDK12
      • JDK13
      • JDK14
      • JDK15
      • JDK16
  • 三、总结

一、简介

由于 SpringBoot 3 最低支持的 JDK 版本是 17 了,加上 JDK 17 是一个 LTS 版本(Long-Term Support,长期稳定可靠的版本),所以还是有必要学习下 JDK 9 - JDK17 的新特性的。写这篇文章帮助自己记忆,也方便后续查阅。

关于 JDK 各版本新特性可查看网址:https://openjdk.org/

二、新特性

下面是我认为比较重要的一些新特性,有些感觉平时不会用的语法糖就不讲了,有兴趣的可以自己去了解了解

接口私有方法(JDK9)

JEP 213: Milling Project Coin

在讲 JDK 9 新特性 —— 接口私有方法之前,我们先了解下在之前的 JDK 版本中接口的变化。

在 JDK 8 之前,我们接口里只存在常量抽象方法

public interface MyInterface {/*** 常量(默认就是public abstract final修饰的)*/String CONSTANT = "常量";/*** 抽象方法(默认就是public abstract修饰的)*/void abstractMethod();
}

在 JDK 8 版本里,增加了默认方法静态方法

默认方法可以定义方法的默认实现,使用 default 关键字修饰

public interface MyInterface {/*** 默认方法*/default void defaultMethod() {System.out.println("这是默认方法");}
}public class MyClass implements MyInterface{public static void main(String[] args) {MyClass myClass = new MyClass();myClass.defaultMethod();}
}

运行结果:

这是默认方法

因为实现类可以不重写接口中的默认方法,所以可以往现存接口中添加新的方法,不用强制那些已经实现了该接口的类也同时实现这个新加的方法,非常棒!

此外还可以在接口里定义静态方法,使用 static 关键字修饰

public interface MyInterface {/*** 静态方法*/static void staticMethod() {System.out.println("这是静态方法");}
}public class MyTest{public static void main(String[] args) {MyInterface.staticMethod();}
}

运行结果:

这是静态方法

有的同学可能已经发现问题了,如果我多个默认方法用到了同一段代码,岂不是要写很多重复代码,于是 JDK 9 在接口里增加了私有方法,可以将相同的代码提取到私有方法里

public interface MyInterface {/*** 默认方法*/default void defaultMethod() {System.out.println("这是默认方法");privateMethod();}/*** 默认方法2*/default void defaultMethod2() {System.out.println("这是默认方法2");privateMethod();}/*** 私有方法*/private void privateMethod() {System.out.println("这是私有方法");}
}public class MyClass implements MyInterface{public static void main(String[] args) {MyClass myClass = new MyClass();myClass.defaultMethod();myClass.defaultMethod2();}
}

运行结果:

这是默认方法
这是私有方法
这是默认方法2
这是私有方法

String存储结构的变化(JDK9)

JEP 254: Compact Strings

String 的存储结构,由 char[] 改成了 byte[],并且增加了一个 coder 属性来表示使用的字符编码

JDK 9 之前

image-20230821154138106

JDK 9 之后

image-20230821154208233

为什么要这么改呢,我们看下文档里是怎么说的

image-20230821154323311

大致的意思就是,原本的存储方式 char 是占2个字节(16位),实际应用程序中字符串又是使用最多的,而大多数的字符串只包含 Latin-1 字符,这个字符只需要1个字节的存储空间,而这种字符串相当于有一半的空间是浪费的。

改版之后,使用byte[],并增加了 coder 属性,这个 coder 有两个可能的值

image-20230821155423373

  • 0:表示字符串对象使用 Latin-1 编码(即只包含 ASCII 字符)
  • 1:表示字符串对象使用 UTF-16 编码(即可以包含任意 Unicode 字符)

对于每个字符串,会先判断它是不是只有 Latin-1 字符。如果是,就按照1字节的规格进行分配内存。如果不是,就按照2字节的规格进行分配。

这样改的话,内存的使用率就提升了,GC的次数会相应的减少。

快速创建只读集合(JDK9、10)

JEP 269: Convenience Factory Methods for Collections

从 JDK 9 开始,集合(List/ Set/ Map)都添加了 of (JDK9添加)和 copyOf (JDK10添加)方法,用来创建不可变的集合

public class MyTest {public static void main(String[] args) {List<String> list = List.of("Java", "Spring", "SpringBoot");System.out.println(list);Set<String> set = Set.of("Java", "Spring", "SpringBoot");System.out.println(set);Map<Integer, String> map = Map.of(1, "Java", 2, "Spring", 3, "SpringBoot");System.out.println(map);List<String> listCopy = List.copyOf(list);System.out.println(listCopy);Set<String> setCopy = Set.copyOf(set);System.out.println(setCopy);Map<Integer, String> mapCopy = Map.copyOf(map);System.out.println(mapCopy);}
}

运行结果:

[Java, Spring, SpringBoot]
[Spring, SpringBoot, Java]
{3=SpringBoot, 1=Java, 2=Spring}
[Java, Spring, SpringBoot]
[Spring, SpringBoot, Java]
{3=SpringBoot, 1=Java, 2=Spring}

JDK 的这个优化想法是不错的,但其实我们日常开发用到只读集合的场景并不多,但是可以按照这个思路,写一个快速创建可变集合的工具类

文本块(JDK13、14、15)

JEP 355: Text Blocks (Preview)

JEP 368: Text Blocks (Second Preview)

JEP 378: Text Blocks

JDK 13 开始预览,JDK 14 二次预览,JDK 15 成为正式版本

目的是简化多行字符串的创建和处理,在旧的 JDK 版本中,我们要创建一个多行字符串,需要用到转义字符和串联操作符来连接多个字符串,例如我们需要创建这样的字符串:

{
“name”: “张三”,
“age”: 18
}

之前我们的写法是这样的:

String text = "{\n" +"  \"name\": \"张三\",\n" +"  \"age\": 18\n" +"}";

而用文本块,我们只需要以三个双引号 """ 开始和结束,中间的内容就是字符串的具体内容,如下:

String text2 = """{"name": "张三","age": 18}""";

更直观的 NullPointerException 提示(JDK14)

JEP 358: Helpful NullPointerExceptions

空指针异常是日常开发中非常常见的异常,但是有个问题,我们不知道具体什么对象是空的,尤其在一行代码上出现多个对象可能为空时,只能通过 debug 代码才能看出来

而 JDK 14 解决了这个问题,优化了 NullPointerException 的提示,可以一眼就看出哪个对象是空的

文档中举的例子还是比较直观的

image-20230822144848690

优化后的提示是类似这样的:

Exception in thread "main" java.lang.NullPointerException: Cannot assign field "i" because "a" is nullat Prog.main(Prog.java:5)

密封类(JDK15、16、17)

JEP 360: Sealed Classes (Preview)

JEP 397: Sealed Classes (Second Preview)

JEP 409: Sealed Classes

JDK 15 开始预览,JDK 16 二次预览,JDK 17 成为正式版本

在 JAVA 里我们可以通过继承来扩展现有类或者实现接口。这种扩展的灵活性使得类的行为变得难以预测,可能导致不必要的错误或安全漏洞

所以就出现了密封类,密封类可以用来限制子类的范围,增强代码的安全性和可维护性

使用 sealed 关键字在类或者接口前进行修饰,使用 permits 关键字来指定可以继承的类

子类也必须用 sealednon-sealedfinal 中的一种关键字修饰

  • sealed:用于声明密封类,只允许有限的子类继承。
  • non-sealed:用于声明普通类,可以被任何类所继承。
  • final:用于声明最终类,禁止继承。
public sealed class Animal permits Dog, Cat {
}public final class Dog extends Animal {
}public final class Cat extends Animal {
}

由于父类是密封类,指定了继承类是 Dog、Cat,这时候如果我写一个 Bird 类继承 Animal 就会报错

instanceof的模式匹配(JDK14、15、16)

JEP 305: Pattern Matching for instanceof (Preview)

JEP 375: Pattern Matching for instanceof (Second Preview)

JEP 394: Pattern Matching for instanceof

instanceof的模式匹配从 JDK 14 开始预览,JDK 15 二次预览,JDK 16 成为正式版本

简化了 instanceof 的书写方式

在 JDK 14 之前,我们需要先判断类型,再做向下转型

if (obj instanceof String) {String s = (String) obj;// use s
}

而 JDK 14 之后,可以将判断和转型合并成一步

if (obj instanceof String s) {// can use s here
}

另外,因为 instanceof 的新变量作用域只在if语句里,如果后面接 && (与操作)可以继续使用instanceof 的变量,如下:

if (obj instanceof String s && s.length() > 5) {.. s.contains(..) ..}

但是由于短路操作,所以后面接||(或操作)就不能使用 instanceof 的变量,因为有可能 instanceof 不满足新变量未定义,如下:

if (obj instanceof String s || s.length() > 5) {.. s.contains(..) ..}

image-20230822160906358

switch增强(JDK12、13、17)

JEP 325: Switch Expressions (Preview)

JEP 354: Switch Expressions (Second Preview)

JEP 361: Switch Expressions

JEP 406: Pattern Matching for switch (Preview)

  • 在 JDK 7 版本之前 switch 语句只能使用整数(byte、short、int)和字符类型(char)作为表达式,并且只支持单个 case 分支,没有 break 会穿透到下一个 case。
  • 在 JDK 7 版本里,增加了支持字符串类型(String)和枚举类型(enum)。
  • 在 JDK 12 版本里,增加了 switch 表达式,用箭头(->)代替冒号(:)
  • 在 JDK 13 版本里,增加了 yield 关键字
  • 在 JDK 17 版本里,支持 switch 类型,简化原本 instanceof 的写法,并且支持 case null,对于枚举类和密封类会进行完整性校验,从而不用写 default 分支

在 JDK 12 之前,我们是这么写的

public static void main(String[] args) {int month = 1;switch (month) {case 1, 2, 3, 4, 5, 6:System.out.println("上半年");break;case 7, 8, 9, 10, 11, 12:System.out.println("下半年");break;default:System.out.println("不存在的月份");break;}
}

为了防止穿透,我们必须要写上 break,而在 JDK 12 之后,用 switch 表达式写,可以省略 break

public static void main(String[] args) {int month = 1;switch (month) {case 1, 2, 3, 4, 5, 6 -> System.out.println("上半年");case 7, 8, 9, 10, 11, 12 -> System.out.println("下半年");default -> System.out.println("不存在的月份");}
}

如果有返回值,可以这么写

public static void main(String[] args) {int month = 1;String text = switch (month) {case 1, 2, 3, 4, 5, 6 -> "上半年";case 7, 8, 9, 10, 11, 12 -> "下半年";default -> "";};System.out.println(text);
}

在 JDK 13版本之后,我们也可以使用 yield 关键字来设置返回值

public static void main(String[] args) {int month = 1;String text = switch (month) {case 1, 2, 3, 4, 5, 6:yield "上半年";case 7, 8, 9, 10, 11, 12:yield "下半年";default:yield "";};System.out.println(text);
}

在 JDK 17 版本之后,对于一个 Object 向下转型时,可以摆脱 instanceof + cast 了

之前的写法

static String formatter(Object o) {String formatted = "unknown";if (o instanceof Integer i) {formatted = String.format("int %d", i);} else if (o instanceof Long l) {formatted = String.format("long %d", l);} else if (o instanceof Double d) {formatted = String.format("double %f", d);} else if (o instanceof String s) {formatted = String.format("String %s", s);}return formatted;
}

JDK 17 之后

static String formatterPatternSwitch(Object o) {return switch (o) {case Integer i -> String.format("int %d", i);case Long l    -> String.format("long %d", l);case Double d  -> String.format("double %f", d);case String s  -> String.format("String %s", s);default        -> o.toString();};
}

另外支持 case null

static void test(Object o) {switch (o) {case null     -> System.out.println("null!");case String s -> System.out.println("String");default       -> System.out.println("Something else");}
}

对于枚举类和密封类会进行完整性校验,从而不用写 default 分支

sealed interface S permits A, B, C {}
final class A implements S {}
final class B implements S {}
record C(int i) implements S {}  // Implicitly finalstatic void switchStatementComplete(S s) {switch (s) {    // Error - incomplete; missing clause for permitted class B!case A a :System.out.println("A");break;case C c :System.out.println("C");break;};
}

如果少写一个 B 的情况,编译期会报错

image-20230822141212160

枚举也是同样的道理

垃圾收回相关优化(JDK9-17)

JDK9

  • 将G1设置为默认垃圾回收器:JEP 248: Make G1 the Default Garbage Collector

JDK10

  • 废弃 CMS 垃圾回收器:JEP 291: Deprecate the Concurrent Mark Sweep (CMS) Garbage Collector
  • 引入G1垃圾回收器的并行Full GC功能:JEP 307: Parallel Full GC for G1

JDK11

  • 革命性的 ZGC 登场:JEP 333: ZGC: A Scalable Low-Latency Garbage Collector (Experimental)

JDK12

  • 引入 Shenandoah GC,低暂停时间的垃圾回收器:JEP 189: Shenandoah: A Low-Pause-Time Garbage Collector (Experimental)

  • G1可以在收集的过程中中止:JEP 344: Abortable Mixed Collections for G1

  • G1自动返回未用堆内存给操作系统:JEP 346: Promptly Return Unused Committed Memory from G1

JDK13

  • ZGC 将未使用的堆内存返回到操作系统:JEP 351: ZGC: Uncommit Unused Memory (Experimental)

JDK14

  • 通过实现 NUMA 感知内存分配,提高大型机器上的G1性能:JEP 345: NUMA-Aware Memory Allocation for G1
  • 删除 CMS 垃圾回收器:JEP 363: Remove the Concurrent Mark Sweep (CMS) Garbage Collector
  • 将 ZGC 垃圾收集器移植到 macOS:JEP 364: ZGC on macOS (Experimental)
  • 将 ZGC 垃圾收集器移植到 Windows:JEP 365: ZGC on Windows (Experimental)
  • 废弃 ParallelScavenge + SerialOld GC 的组合:JEP 366: Deprecate the ParallelScavenge + SerialOld GC Combination

JDK15

  • ZGC 正式上线:JEP 377: ZGC: A Scalable Low-Latency Garbage Collector (Production)
  • Shenandoah GC 正式上线:JEP 379: Shenandoah: A Low-Pause-Time Garbage Collector (Production)

JDK16

  • ZGC 支持并发栈处理:JEP 376: ZGC: Concurrent Thread-Stack Processing

三、总结

JDK 9-17 增加了许多新的特性,对我们的日常开发有很大帮助,JDK 11推出的 ZGC 是一款性能比 G1 还要好的垃圾回收器,JDK 17 也是史上最快的 Java 版本。

Spring Boot 3.0 的最低支持版本已经提升到 JDK 17,并且不再向下兼容 JDK 8,开发者从 JDK 8 转移到 JDK 17 将是大势所趋,我们不能畏惧新的东西,保持学习的姿态,通过阅读文档和实践,不断的进步!


http://www.ppmy.cn/news/1073778.html

相关文章

音视频 ffmpeg命令提取PCM数据

提取PCM ffmpeg -i buweishui.mp3 -ar 48000 -ac 2 -f s16le 48000_2_s16le ffmpeg -i buweishui.mp3 -ar 48000 -ac 2 -sample_fmt s16 out_s16.wav ffmpeg -i buweishui.mp3 -ar 48000 -ac 2 -codec:a pcm_s16le out2_s16le.wav ffmpeg -i buweishui.mp3 -ar 48000 -ac 2 -f…

近期嵌软线下笔试题记录

1、以下代码的输出结果是&#xff1f; #include <stdio.h> #include <string.h>int main() {int a,b,c,d;a 10;b a; //a先赋值给b,然后自增1c a; //a自增1后赋值给cd 10*a; //先进行运算然后a自增1printf("b,c,d:%d…

【Flutter】Flutter 使用 fluttertoast 实现显示 Toast 消息

【Flutter】Flutter 使用 fluttertoast 实现显示 Toast 消息 文章目录 一、前言二、安装和基础使用三、不同平台的支持情况四、如何自定义 Toast五、在实际业务中的应用六、完整的业务代码示例&#xff08;基于 Web 端&#xff09;七、总结 一、前言 在这篇文章中&#xff0c;…

Linux- 文件夹相关的常用指令

1. 统计文件夹下的文件数量 在 Linux 下&#xff0c;有几种方法可以统计文件夹下的文件数量&#xff1a; 使用 ls 和 wc 命令&#xff1a; 这种方式可以统计目录下的直接子文件&#xff08;不包括子目录里的文件&#xff09;。 ls -l <目录路径> | wc -l注意&#xff1a…

【SLAM轨迹误差】rpg_trajectory_evaluation

下载地址&#xff1a;https://www.zora.uzh.ch/id/eprint/175991/1/IROS18_Zhang.pdf Z. Zhang and D. Scaramuzza, “A Tutorial on Quantitative Trajectory Evaluation for Visual(-Inertial) Odometry,” 2018 IEEE/RSJ International Conference on Intelligent Robots an…

RabbitMQ工作模式-主题模式

主题模式 官方文档参考&#xff1a;https://www.rabbitmq.com/tutorials/tutorial-five-python.html 使用topic类型的交换器&#xff0c;队列绑定到交换器、bingingKey时使用通配符&#xff0c;交换器将消息路由转发到具体队列时&#xff0c;会根据消息routingKey模糊匹配&am…

爬虫逆向实战(二十六)--某某学堂登录

一、数据接口分析 主页地址&#xff1a;某某学堂 1、抓包 通过抓包可以发现数据接口是Account/LoginPost 2、判断是否有加密参数 请求参数是否加密&#xff1f; 通过查看“载荷”模块可以发现pass是加密参数 请求头是否加密&#xff1f; 无响应是否加密&#xff1f; 无co…

记一次对链接、COMMON块、多重符号定义的理解

问题引入 首先是两个测试程序 // foo.c long long int a;// bar.c #include <stdio.h>int a; int main(){a 1;long long int len sizeof(a);printf("%lld\n", len);return 0; }将两个程序链接到一起 问题&#xff1a;len等于几&#xff1f; 初步分析 环境…