JDK17 里藏着这些好用的新特性,程序员该知道

作为一个写了几年 Java 的程序员,每次 JDK 出新版本我都会格外关注。尤其是 JDK17 这种长期支持版本(LTS),就像手机系统里的稳定版,会被许多公司用好几年。今天就用大白话聊聊 JDK17 里那些实用的新特性,不管是刚入行的新手还是老程序员,说不定都能用上。

写代码更顺手了,这两个语法糖要试试

密封类:终于能管住那些乱继承的类了

以前写代码时,定义一个抽象类总怕别人乱继承。列如我定义了个Shape类,本意是只让Circle、Rectangle这几个类继承,结果总有同事为了图方便,随意写个类就去继承,到最后代码乱得没法维护。

JDK17 的密封类(Sealed Classes)就是来解决这个问题的。用sealed关键字声明的类,能明确指定哪些子类可以继承它,其他类想继承都不行。

// 只允许Circle、Rectangle、Triangle继承

public sealed class Shape

    permits Circle, Rectangle, Triangle {

    public abstract double area();

}

// 子类必须声明成final(不能再被继承)

public final class Circle extends Shape {

    private final double radius;

    public Circle(double radius) {

        this.radius = radius;

    }

    @Override

    public double area() {

        return Math.PI * radius * radius;

    }

}

这样一来,类的继承关系就清清楚楚,别人想乱改都难。编译器还会帮你检查,要是子类不在permits列表里,直接就报错,从源头避免了混乱。

switch 表达式升级,不用再写一堆 if-else 了

以前处理不同类型的数据时,总得写一串if (obj instanceof …),不仅啰嗦,还容易出错。JDK17 里的 switch 表达式支持了类型匹配,代码一下子清爽了不少。

列如要格式化不同类型的数据,以前得这么写:

public String format(Object obj) {

    if (obj instanceof Integer i) {

        return “数字:” + i;

    } else if (obj instanceof String s) {

        return “字符串:” + s;

    } else {

        return “不知道啥类型”;

    }

}

目前用 switch 改写,是不是简洁多了:

public String formatWithSwitch(Object obj) {

    return switch (obj) {

        case Integer i -> “数字:” + i;

        case String s -> “字符串:” + s;

        case null -> “啥都没有”;

        default -> “不知道啥类型”;

    };

}

最贴心的是还能直接处理 null 值,以前写 switch 时要是传个 null 进去,肯定会报空指针异常,目前专门加了case null,再也不用提前判断了。

程序跑得更快了,这些优化藏在细节里

ZGC 垃圾回收器:大内存应用的救星

以前做一个电商项目,服务器内存用到 32G 以上时,垃圾回收常常卡顿,用户下单时偶尔会遇到几秒的延迟,投诉不少。后来换成 JDK17 的 ZGC,情况好了许多。

ZGC 是个低延迟的垃圾回收器,在 JDK17 里又做了升级:不管堆内存是 8MB 还是 16TB,回收时的停顿时间都能控制在毫秒以内。我们把服务器的堆内存调到 64G 后,用 ZGC 跑了一个月,高峰期的响应时间标准差比以前降了快一半,用户几乎感觉不到卡顿。

启用也很简单,启动时加个参数就行:-XX:+UseZGC。如果是新项目,强烈提议试试;老项目升级时,这个参数能解决不少性能问题。

矢量 API:让 CPU 全力干活

这个特性可能平时用得不多,但做数据分析、图像处理的同学会很喜爱。简单说,就是让 CPU 一次能处理多个数据,列如同时给一个数组的所有元素做加法,速度能比普通循环快好几倍。

列如计算一个 float 数组的总和,用矢量 API 来写:

public static float sum(float[] array) {

    // 选一个合适的矢量规格

    int species = FloatVector.SPECIES_PREFERRED;

    int length = array.length;

    int i = 0;

    // 初始化一个求和的矢量

    FloatVector sumVec = FloatVector.zero(species);

    // 批量处理数组元素

    for (; i <= length – species.length(); i += species.length()) {

        FloatVector vec = FloatVector.fromArray(species, array, i);

        sumVec = sumVec.add(vec);

    }

    // 处理剩下的元素

    float sum = sumVec.reduceLanes(VectorOperators.ADD);

    for (; i < length; i++) {

        sum += array[i];

    }

    return sum;

}

我们测试时用了一个 100 万元素的数组,这段代码比普通 for 循环快了 3 倍多。不用写复杂的 native 代码,纯 Java 就能利用 CPU 的高级特性,这波优化很实在。

更安全了,但这些老代码得改改

内部 API 不让随意用了

以前有些程序员喜爱用sun.misc这类内部 API,觉得方便。但这些 API 不是公开标准,说不定哪个版本就变了。JDK17 里把这些 API 默认封起来了,想访问就得专门加参数,而且还会警告。

列如以前用sun.misc.Unsafe的地方,目前最好换成java.util.concurrent里的类。我去年升级项目时,用jdeps –jdk-internals命令查了一下,发现有十几个地方用到了内部 API,花了一周时间才改完。虽然麻烦,但长远来看,能避免后来升级时出大问题。

Security Manager 被移除了

这个东西从 JDK1.0 就有了,主要用来做安全控制,但目前几乎没人用了。JDK17 干脆把它移除了,要是老项目里还有相关代码,得赶紧换成别的方案,列如用模块权限控制,或者在服务器层面做隔离。

还有这些小改善,用起来挺舒服

启动更快了:JDK17 优化了类加载机制,大型应用的启动时间能缩短 15% 左右。我们公司的一个 Spring Boot 项目,以前启动要 30 多秒,升级后不到 26 秒就起来了。

随机数生成更方便:新增了RandomGenerator接口,支持好几种随机算法,生成随机数更灵活,还能保证线程安全。

Applet API 被废弃了:这个估计没人用了,毕竟目前浏览器都不支持 Java 插件了,删了也清净。

最后说点升级的小提议

如果打算把项目升到 JDK17,提议先做这几件事:

用jdeps工具检查一下有没有依赖内部 API,早发现早修改。

升级依赖的框架,列如 Spring Boot 至少要 2.6 以上,Hibernate 得 5.6 以上才行。

试试 ZGC 垃圾回收器,特别是内存大的应用,性能提升很明显。

旧代码里的if-else类型判断,慢慢换成新的 switch 表达式,代码会更清爽。

JDK17 作为长期支持版本,会维护到 2029 年,值得花时间升级。刚开始可能会有点小麻烦,但用熟了就会发现,这些新特性是真的能提高开发效率,还能让程序跑得更快更稳。

你们项目升级 JDK17 了吗?遇到了哪些问题?来评论区聊聊~

© 版权声明

相关文章

暂无评论

none
暂无评论...