300 秒快速了解 Java 9 - 16 新特性

点赞再看,养成好习惯java

JAVA 这几年的更新实在是太太太……快了,JAVA 8 都还没用多久,16 都已经发布了。自从 JAVA 8 发布了 Lambda 和 Stream 以后,JAVA 就像打了鸡血同样,半年一个版本的发布,生产队的驴也没这么勤快。 ​json

image (1).png

致使咱们如今彻底跟不上 JAVA 发布的节奏,我司目前还停留在 JAVA 8,甚至部分老系统还在使用 JAVA 7,根本不能轻易的升级。 ​安全

不过虽然暂时用不上最新版本的 JAVA,但了解每一个新版本的主要特性仍是很重要的,否则哪天真跟着升级了,那还不得一脸懵逼。 ​bash

本文就带你快速了解 JAVA 9 - 16 的主要新特性,早学完早下班!markdown

JAVA 9(2017年9月)

接口里能够添加私有接口

JAVA 8 对接口增长了默认方法的支持,在 JAVA 9 中对该功能又来了一次升级,如今能够在接口里定义私有方法,而后在默认方法里调用接口的私有方法。 ​app

这样一来,既能够重用私有方法里的代码,又能够不公开代码ide

public interface TestInterface {
    default void wrapMethod(){
        innerMethod();
    }
    private void innerMethod(){
        System.out.println("");
    }
}
复制代码

匿名内部类也支持钻石(diamond)运算符

JAVA 5 就引入了泛型(generic),到了 JAVA 7 开始支持钻石(diamond)运算符:<>,能够自动推断泛型的类型:函数

List<Integer> numbers = new ArrayList<>();
复制代码

可是这个自动推断类型的钻石运算符可不支持匿名内部类,在 JAVA 9 中也对匿名内部类作了支持:工具

List<Integer> numbers = new ArrayList<>() {
    ...
}
复制代码

加强的 try-with-resources

JAVA 7 中增长了try-with-resources的支持,能够自动关闭资源:post

try (BufferedReader bufferReader = new BufferedReader(...)) {
    return bufferReader.readLine();
}
复制代码

但须要声明多个资源变量时,代码看着就有点恶心了,须要在 try 中写多个变量的建立过程:

try (BufferedReader bufferReader0 = new BufferedReader(...);
    BufferedReader bufferReader1 = new BufferedReader(...)) {
    return bufferReader0.readLine();
}
复制代码

JAVA 9 中对这个功能进行了加强,能够引用 try 代码块以外的变量来自动关闭:

BufferedReader bufferReader0 = new BufferedReader(...);
BufferedReader bufferReader1 = new BufferedReader(...);
try (bufferReader0; bufferReader1) {
    System.out.println(br1.readLine() + br2.readLine());
}
复制代码

JAVA 10(2018年3月)

局部变量的自动类型推断(var)

JAVA 10 带来了一个颇有意思的语法 - var,它能够自动推断局部变量的类型,之后不再用写类型了,也不用靠 lombok 的 var 注解加强了

var message = "Hello, Java 10";
复制代码

不过这个只是语法糖,编译后变量仍是有类型的,使用时仍是考虑下可维护性的问题,否则写多了可就成 JavaScript 风格了

JAVA 11(2018年9月)

Lambda 中的自动类型推断(var)

JAVA 11 中对 Lambda 语法也支持了 var 这个自动类型推断的变量,经过 var 变量还能够增长额外的注解:

List<String> languages = Arrays.asList("Java", "Groovy");
String language = sampleList.stream()
  .map((@Nonnull var x) -> x.toUpperCase())
  .collect(Collectors.joining(", "));

assertThat(language).isEqualTo("Java, Groovy");
复制代码

javac + java 命令一把梭

之前编译一个 java 文件时,须要先 javac 编译为 class,而后再用 java 执行,如今能够一把梭了:

$ java HelloWorld.java
Hello Java 11!
复制代码

Java Flight Recorder 登录 OpenJDK

Java Flight Recorder 是个灰常好用的调试诊断工具,不过以前是在 Oracle JDK 中,也跟着 JDK 11 开源了,OpenJDK 这下也能够用这个功能,真香!

JAVA 12(2019年3月)

更简洁的 switch 语法

在以前的 JAVA 版本中,switch 语法仍是比较啰嗦的,若是多个值走一个逻辑须要写多个 case

DayOfWeek dayOfWeek = LocalDate.now().getDayOfWeek();
String typeOfDay = "";
switch (dayOfWeek) {
    case MONDAY:
    case TUESDAY:
    case WEDNESDAY:
    case THURSDAY:
    case FRIDAY:
        typeOfDay = "Working Day";
        break;
    case SATURDAY:
    case SUNDAY:
        typeOfDay = "Day Off";
}
复制代码

到了 JAVA 12,这个事情就变得很简单了,几行搞定,并且!还支持返回值:

typeOfDay = switch (dayOfWeek) {
    case MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY -> "Working Day";
    case SATURDAY, SUNDAY -> "Day Off";
};
复制代码

instanceof + 类型强转一步到位

以前处理动态类型碰上要强转时,须要先 instanceof 判断一下,而后再强转为该类型处理:

Object obj = "Hello Java 12!";
if (obj instanceof String) {
    String s = (String) obj;
    int length = s.length();
}
复制代码

如今 instanceof 支持直接类型转换了,不须要再来一次额外的强转:

Object obj = "Hello Java 12!";
if (obj instanceof String str) {
    int length = str.length();
}
复制代码

JAVA 13(2019年9月)

switch 语法再加强

JAVA 12 中虽然加强了 swtich 语法,但并不能在 -> 以后写复杂的逻辑,JAVA 12 带来了 swtich更完美的体验,就像 lambda 同样,能够写逻辑,而后再返回:

typeOfDay = switch (dayOfWeek) {
    case MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY -> {
        // do sth...
    	yield "Working Day";
    }
    case SATURDAY, SUNDAY -> "Day Off";
};
复制代码

文本块(Text Block)的支持

你是否还在为大段带换行符的字符串报文所困扰,换行吧一堆换行符,不换行吧看着又难受:

String json = "{\"id\":\"1697301681936888\",\"nickname\":\"空无\",\"homepage\":\"https://juejin.cn/user/1697301681936888\"}";
复制代码

JAVA 13 中帮你解决了这个恶心的问题,增长了文本块的支持,如今能够开心的换行拼字符串了,就像用模板同样:

String json = """ { "id":"1697301681936888", "nickname":"空无", "homepage":"https://juejin.cn/user/1697301681936888"
				}
				"""; 复制代码

JAVA 14(2020年3月)

新增的 record 类型,干掉复杂的 POJO 类

通常咱们建立一个 POJO 类,须要定义属性列表,构造函数,getter/setter,比较麻烦。JAVA 14 为咱们带来了一个便捷的建立类的方式 - record

public record UserDTO(String id,String nickname,String homepage) { };

public static void main( String[] args ){
	UserDTO user = new UserDTO("1697301681936888","空无","https://juejin.cn/user/1697301681936888");
    System.out.println(user.id);
    System.out.println(user.nickname);
    System.out.println(user.id);
}
复制代码

IDEA 也早已支持了这个功能,建立类的时候直接就能够选: image (2) (1).png 不过这个只是一个语法糖,编译后仍是一个 Class,和普通的 Class 区别不大

更直观的 NullPointerException 提示

NullPointerException 算是 JAVA 里最多见的一个异常了,但这玩意提示实在不友好,遇到一些长一点的链式表达式时,没办法分辨究竟是哪一个对象为空。 ​

好比下面这个例子中,究竟是 innerMap 为空呢,仍是 effected 为空呢?

Map<String,Map<String,Boolean>> wrapMap = new HashMap<>();
wrapMap.put("innerMap",new HashMap<>());

boolean effected = wrapMap.get("innerMap").get("effected");

// StackTrace:
Exception in thread "main" java.lang.NullPointerException
	at org.example.App.main(App.java:50)
复制代码

JAVA 14 也 get 到了 JAVAER 们的痛点,优化了 NullPointerException 的提示,让你不在困惑,一眼就能定位到底“空”在哪!

Exception in thread "main" java.lang.NullPointerException: Cannot invoke "java.lang.Boolean.booleanValue()" because the return value of "java.util.Map.get(Object)" is null
	at org.example.App.main(App.java:50)
复制代码

如今的 StackTrace 就很直观了,直接告诉你 effected 变量为空,不再用困惑!

安全的堆外内存读写接口,别再玩 Unsafe 的骚操做了

在以前的版本中,JAVA 若是想操做堆外内存(DirectBuffer),还得 Unsafe 各类 copy/get/offset。如今直接增长了一套安全的堆外内存访问接口,能够轻松的访问堆外内存,不再用搞 Unsafe 的骚操做了。

// 分配 200B 堆外内存
MemorySegment memorySegment = MemorySegment.allocateNative(200);

// 用 ByteBuffer 分配,而后包装为 MemorySegment
MemorySegment memorySegment = MemorySegment.ofByteBuffer(ByteBuffer.allocateDirect(200));

// MMAP 固然也能够
MemorySegment memorySegment = MemorySegment.mapFromPath(
  Path.of("/tmp/memory.txt"), 200, FileChannel.MapMode.READ_WRITE);

// 获取堆外内存地址
MemoryAddress address = MemorySegment.allocateNative(100).baseAddress();

// 组合拳,堆外分配,堆外赋值
long value = 10;
MemoryAddress memoryAddress = MemorySegment.allocateNative(8).baseAddress();
// 获取句柄
VarHandle varHandle = MemoryHandles.varHandle(long.class, ByteOrder.nativeOrder());
varHandle.set(memoryAddress, value);

// 释放就这么简单,想一想 DirectByteBuffer 的释放……多奇怪
memorySegment.close();
复制代码

不了解 Unsafe 操做堆外内存方式的同窗,能够参考个人另外一篇文章《JDK中为了性能大量使用的Unsafe类,你会用吗?

新增的 jpackage 打包工具,直接打包二进制程序,不再用装 JRE 了

以前若是想构建一个可执行的程序,还须要借助三方工具,将 JRE 一块儿打包,或者让客户电脑也装一个 JRE 才能够运行咱们的 JAVA 程序。 ​

如今 JAVA 直接内置了 jpackage 打包工具,帮助你一键打包二进制程序包,终于不用乱折腾了

JAVA 15(2020年9月)

ZGC 和 Shenandoah 两款垃圾回收器正式登录

在 JAVA 15中,ZGC 和 Shenandoah 不再是实验功能,正式登录了(不过 G1 仍然是默认的)。若是你升级到 JAVA 15 之后的版本,就赶快试试吧,性能更强,延迟更低

封闭(Sealed )类

JAVA 的继承目前只能选择容许继承和不容许继承(final 修饰),如今新增了一个封闭(Sealed )类的特性,能够指定某些类才能够继承:

public sealed interface Service permits Car, Truck {

    int getMaxServiceIntervalInMonths();

    default int getMaxDistanceBetweenServicesInKilometers() {
        return 100000;
    }

}
复制代码

JAVA 16(2021年3月)

JAVA 16 在用户可见的地方变化并很少,基本都是 14/15 的实验性内容,到了 16 正式发布,这里就不重复介绍了。 ​

总结

以上介绍的各类新特性,有些特性在历史版本中还属于实验性功能,不过按照 JAVA 目前这个驴同样的更新频率,极可能下个版本就是稳定版了。早学早享受,晚学被卷走……

看看时间,300 秒到了吗?

原创不易,禁止未受权的转载。若是个人文章对您有帮助,就请点赞/收藏/关注鼓励支持一下吧❤❤❤❤❤❤

相关文章
相关标签/搜索