DEV Community

[yun]
[yun]

Posted on • Edited on

JDK 8 -> 20 最重用的新增特性

Java 8 早已经在 2014 年发布,毫无疑问 Java 8 对 Java 来说绝对算得上是一次重大版本更新,它包含了十多项语言、库、工具、JVM 等方面的十多项新特性。比如提供了语言级的匿名函数,也就是被官方称为 Lambda 的表达式语法,比如函数式接口,方法引用,重复注解。再比如 Optional 预防空指针,Stearm 流式作,LocalDateTime 时间操作等等。

如今,在2023年3月21日,Java 20已正式发布,但不是一个长期支持版本(LTS)。目前的长期支持版是Java 17。在这篇章LTS不是重点,我们把中重点放在从Java 8发展到现在的Java 20到底新增什么重要的特性。这里,我只总结了11个我个人认为重要的标准新特性(不是预览的),只要你是用Java 17,这些新特性你都能用得上。

Table Of Contents


JDK 9

集合工厂方法

JEPS-269 : Convenience Factory Methods for Collections

在 JDK 9 中为集合的创建增加了静态工厂创建方式(Collection Factories)。这个集合工厂可以只用一行代码来创建一些较小的集合,目前支持List, SetMap。这个方法只适合用于创建只读集合(Immutable Collection),里面的对象不可改变。

在 JDK 8 中:

Set<String> mySet = new HashSet<String>();
mySet.add("yun");
mySet.add("yyx");
mySet.add("andy");
mySet = Collections.unmodifiableSet(mySet);
Enter fullscreen mode Exit fullscreen mode

而在 JDK 9 中只需:

Set<String> mySet = Set.of("yun", "yyx", "andy");
Enter fullscreen mode Exit fullscreen mode

回顶部


JDK 10

局部类型推断

JEPS-286 : Local-Variable Type Inference

如何你是 Javascript 程序员,你一定熟悉var这个syntax。在 JDK 10 也加入var来实现自动推断数据类型。但这个还是和 Javascript 的var很不一样的。在 Java 里只是一个新的语法,它在编译时就把var根据转化成具体的数据类型了。

传统写法:

String str = "https://dev.to/";
URL url = new URL(str);
URLConnection con = url.openConnection();
Enter fullscreen mode Exit fullscreen mode

JDK 10简化版:

var str = "https://dev.to/";
var url = new URL(str);
var con = url.openConnection();
Enter fullscreen mode Exit fullscreen mode

虽然var简化了 Java 代码,但也必须知道var的使用场景和避免危险使用。比如以下的写法是有后患的:

var flag = 0; // 这里默认是int,而不是short或byte
var items = new ArrayList<>(); // 这里默认是ArrayList<Object>
Enter fullscreen mode Exit fullscreen mode

OpenJDK官方也给出了LVTI的使用指南,往后我再整理一篇简介的文章。

回顶部


JDK 14

Switch 表达式

JEPS-361: Switch Expression

Switch 表达式早在 JDK 12 就推出了,而正式标准化是在 JDK 14。主要是加入了->yeild语法在switch语法里。

传统写法:

String season;
switch(month){
    case MAR:
    case APR:
    case MAY:
        season = "spring";
        break;
    case JUN:
    case JUL:
    case AUG:
        season = "summer";
        break;
    case SEP:
    case OCT:
    case NOV:
        season = "autumn";
        break;
    case DEC:
    case JAN:
    case FEB:
        season = "winter";
        break;
    default:
        throw new IllegalArgumentException("Not a month: " + month);
}
return season;
Enter fullscreen mode Exit fullscreen mode

Switch 表达式简化后:

return switch(month){
    case MAR, APR, MAY -> "spring";
    case JUN, JUL, AUG -> "summer";
    case SEP, OCT, NOV -> "autumn";
    case DEC, JAN, FEB -> "winter";
}
Enter fullscreen mode Exit fullscreen mode

回顶部

更清晰的NPE

JEPS-358: Helpful NPE

在 JDK 14 之前,NullPointerException(NPE)只汇报哪一行代码导致空指针,在有多个表达式的代码里是很难知道那个对象为null。在 JDK 14,这个 exception 已加入清晰的 message 告诉你哪个对象是null

比方这行代码,如果bnull

a.i = b.j;
Enter fullscreen mode Exit fullscreen mode

在 JDK 14 之前的报错是这样的:

Exception in thread "main" java.lang.NullPointerException
    at App.main(App.java:5)
Enter fullscreen mode Exit fullscreen mode

而 JDK 14 清晰的表明:

Exception in thread "main" java.lang.NullPointerException: 
    Cannot read field "j" because "b" is null
    at App.main(App.java:5)
Enter fullscreen mode Exit fullscreen mode

回顶部


JDK 15

文本块

JEPS-378: Text Blocks

在 Java 里长文本字符串连接一直以来都是一个恶梦,因为不仅写起来麻烦,读起来也很恶心。终于在 JDK 15 加入了文本块。直接来看看对比把。

之前的字符串连接:

String json = "{\n" +
    "\"name\": \"" + user.name + "\",\n" +
    "\"nickname\": \"" + user.nickName + "\"\n" + 
    "}";
Enter fullscreen mode Exit fullscreen mode

JDK 15 的文本块:

var json = """
        {
            "name": "%s",
            "nickname": "%s"
        }
        """.formatted(user.name, user.nickName);
Enter fullscreen mode Exit fullscreen mode

回顶部

ZGC: 可扩展低延迟垃圾收集器

JEPS-377: A Scalable Low-Latency Garbage Collector

在 JDK 15,ZGC 垃圾收集器正式发布。根据 SPEC (Standard Performance Evaluation Corporation) 的测试,ZGC 拥有着不俗的性能:

Image description

而且还在 JDK 版本更新中不断优化:

Image description

那么 ZGC 那么好,为什么没有取代 G1GC 作为默认的 GC 呢?我个人认为有几个原因。GC 在 Java 里是一个很深学问,目前没有一个方案可以解决全部的使用场景。而 G1GC 已被广泛使用,表现也比较稳定,如果突然默认改了去用 ZGC 可能会出现不可预算的问题而导致大量不太了解 GC 的 Java 用户陷入困境。也有很多 Java 用户做 GC 调试只在默认 GC 上做调试,如果冒昧的升级用了不同的 GC,不读升级文档的 Java 用户酱不懂发生了什么事导致调试失效。

回顶部


JDK 16

instanceof 模式匹配

JEPS-394: Pattern Matching for instanceof

这个 Pattern Matching 的升级略为直接,就是简化了instanceof使用场景。这里直接举例:

传统写法:

if( obj instanceof String){ // 1) 检查 obj 的类
    // 2) declare 新的 String 变量 str,3) 并 cast obj 去 String 类
    String str = (String) obj; 

    // 使用 str 
}
Enter fullscreen mode Exit fullscreen mode

上面的 3 步现在可以简化成 1 步了:

if( obj instanceof String str ){
    // 直接可以使用 str 
}
Enter fullscreen mode Exit fullscreen mode

回顶部

Record 类

JEPS-395: Records

JDK 16 加入了record类。我相信大部分 Java 用户都用过Lombok工具库吧,这个record类和Lombok@Data类似,它会自动编译出hashcodeequalstoString 等方法。但record是一个final class,同时所有的属性都是final修饰,所以record只有getter而已。更符合 Record 这么名词,就是不可更改(Immutable)的。

JDK 16 之前:

final class Point {
    private final int x;
    private final int y;

    Point(int x, int y) {
        this.x = x;
        this.y = y;
    }

    int x() { return x; }
    int y() { return y; }

    public boolean equals(Object o) {
        if (!(o instanceof Point)) return false;
        Point other = (Point) o;
        return other.x == x && other.y == y;
    }

    public int hashCode() {
        return Objects.hash(x, y);
    }

    public String toString() {
        return String.format("Point[x=%d, y=%d]", x, y);
    }
}
Enter fullscreen mode Exit fullscreen mode

Lombok写法:

@Getter
@ToString
@EqualsAndHashCode
@AllArgsConstructor
final class Point{
    final int x;
    final int y;
}
Enter fullscreen mode Exit fullscreen mode

JDK 16 record写法:

record Point(int x, int y) { }
Enter fullscreen mode Exit fullscreen mode

回顶部


JDK 17

Java 17 是一个长期支持(LTS)版本,有较多的更新,这里我只列出两个我认为比较值得一提的特性。

密封类

JEPS-409: Sealed Classes

sealed 密封类用来限制classinterface的继承的机制,可以说是灵活版的final。被sealed修饰的类可以指定子类,而且sealed修饰的类的机制具有传递性,而它的子类必须使用finalsealednon-sealed来修饰。

打个比方:

public abstract sealed class Shape
    permits Circle, Rectangle, Square, WeirdShape { ... }

public final class Circle extends Shape { ... }

public sealed class Rectangle extends Shape 
    permits TransparentRectangle, FilledRectangle { ... }

public final class TransparentRectangle extends Rectangle { ... }
public final class FilledRectangle extends Rectangle { ... }

public final class Square extends Shape { ... }

public non-sealed class WeirdShape extends Shape { ... }
Enter fullscreen mode Exit fullscreen mode

回顶部

Switch 模式匹配 (预览)

JEPS-406: Pattern Matching for switch (Preview)

虽然在 JDK 16 加入了instanceof模式匹配,但有些使用场景,写起代码来还是比较麻烦的。在 JDK 17 把模式匹配引入到switch里。虽然这个是预览,但我觉得这个特性挺有用的,就说说这个用在switch里的模式匹配。

使用instanceof模式匹配:

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;
}
Enter fullscreen mode Exit fullscreen mode

新的switch写法简洁很多:

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();
    };
}
Enter fullscreen mode Exit fullscreen mode

回顶部


JDK 18

默认 UTF-8 字符编码

JEPS-400: UTF-8 by Default

最后提一提用纯英文写代码的不太关心的改进,而用中文写代码需要知道的,就是默认字符编码。在 JDK 18 把默认字符编码设置成了 UTF-8。这样有用字符编码的 APIs 都能在开发,操作系统和配置上保持一致性。

回顶部

总结

JDK 17 已改进与新增了挺多很有用的特性,以上是我个人认为帮助到我平日写代码的,不仅能写出更清晰的代码(readability),在性能上也有大幅的提升。
如果你还用着 JDK 8,强力建议尽快升级到 JDK 17,一般上是不会有太多的问题,必须注意的是 GC,也许默认的 GC 是使用 Serial GC,而新版是使用 G1GC。

回顶部

官方视频介绍:
https://www.youtube.com/watch?v=NA-sB3zvluE

Top comments (0)