前言:按照博主的个人理解,一般来说 除了jdk8时代 说jdk8的新特性是特指jdk8这一个版本的特性,之后例如jdk11 jdk17新特性 都是泛特性
什么意思呢? 比如jdk11新特性,一般是指jdk9——jdk11 这一个泛版本的所有新特性,就jdk9引入的 List.of Map.of等api 我们也统称是jdk11的新特性
文章目录
- 为什么大家只提及这几个版本
- jdk11主要新特性
- jdk17主要新特性
- jdk21主要新特性
- 虚拟线程
为什么大家只提及这几个版本
为什么我们很少听到jdk9,jdk10…等其它版本呢
个人认为被大家广为流传的版本需要同时满足以下两点:
- 有重大更新的版本 比如典型的jdk8时代带来的lambda等特性
- 是LTS版本 (长期支持版本) 作为企业 肯定也需要考虑这点
jdk11主要新特性
这里说的是泛特性 即jdk9——jdk11
主要例举博主接触到的,应该也是我们比较常见的一些情况
-
新增了List.of , Map.of 等api,简化了代码 (jdk9特性)
(List.of()类似Arrays.asList , 都是不可删除的list) -
stream流api更新
在旧版本的jdk中,stream流有个致命的缺点 不能break! 这使得我们在需要break的时候及其不方便, 有了takeWhile方法 可以一定程度上实现break。
List<Integer> list = Stream.of(4, 2 ,3).takeWhile(i->(i<3)).toList();
// 输出空list
System.out.println(list);
List<Integer> list1 = Stream.of(1, 2 ,3).takeWhile(i->(i<3)).toList();
// 输出 [1,2]
System.out.println(list1);
但是注意!它是按顺序来执行的,如果第一个元素都不符合 就直接break了
我们可以举个例子
List<String> nameList = List.of("小帅","孟秋与你","小丑");
// break写法:
for(String name : nameList){
if("孟秋与你".equals(name)){
// 返回一个新的数组
return List.of(name);
break;
}
}
// 但是如果用takeWhile 将会返回空数组!
// 因为第一个元素"小帅"就不符合我们的判断条件 直接退出了
-
jdk部分模块被移除 需要单独引入
例如javaFx被移除 (主要是桌面开发相关的api) jaxb涉及的也被移除 (这个主要是xml相关的api) rmi 相关的包也被移除 (这个日常开发基本用不到 ,对我们来说主要是学习如何防止被rmi远端执行 例如著名的log4j事件 黑客就是通过rmi执行的) 其它模块的移除...
-
默认垃圾收集器的变化
说的就是G1 gc收集器 -
接口里面支持private方法
这个可以视为jdk8新增default 方法的一个补充,比如default方法过于庞大,这个时候就可能需要一个private方法抽取逻辑了。
public interface Test{
default void test() {
// do other
testHandle();
}
private void testHandle() {
// do other
}
}
- 语法的简单变化
这个基本就是泛型加一个类型减一个类型的事了,具体情况不记得了,之前遇过一次,不过影响不大 遇到的话 启动代码的时候如果报错就知道了,非常容易判断并修改的
jdk17主要新特性
同样指的是泛特性 jdk12——jdk17
-
NPE优化提示
这个可以说非常友好了, 在链式调用时 会准确提示NPE的位置
旧版jdk (此处指 jdk<= 11) 的提示:
-
文本块
这个在编写长文本的时候非常有用
旧:
return "<html>\n" +
" <body>\n" +
" <p>csdn: 孟秋与你</p>\n" +
" </body>\n" +
"</html>";
新:
return """
<html>
<body>
<p>csdn: 孟秋与你</p>
</body>
</html>
""";
- stream流Api进一步更新
可以直接使用 toList()方法了,不需要使用collect(Collectors.toList())这么麻烦
List<Integer> list = Stream.of(4, 2 ,3).takeWhile(i->(i<3)).toList();
- switch优化
旧写法 每个分支都要写break 及其臃肿
public String test() {
String res = "";
switch (1){
case 1:
res="打工";
break;
// ....
case 6:
res="休息";
break;
default:
res = "打工";
break;
}
return res;
}
新写法:
(注意语法 箭头指向的内容即表明了返回值 所以是在return的时候使用该写法)
public String test() {
return switch (1) {
case 1 ->"上班";
case 6 -> "休息";
default -> "上班";
};
}
-
sealed、permits关键字
虽然这是jdk17的新特性,但是博主是在jdk21虚拟线程相关的源码中才看到的,这两个关键字主要是用来限制继承关系的,相当于给继承加了个枚举限制,用个例子来解释就特别容易理解
/**
* sealed + permits 配合使用:
* 表示Animal类只可以被 Cat 和 Dog类继承
* Cat类 和 Dog类需要声明为final (否则可能被多重继承)
* 若Cat和Dog类没声明为final 或其它类继承了Animal 会报编译时错误
*/
public sealed class Animal permits Cat,Dog {
}
public final class Cat extends Animal{
}
public final class Dog extends Animal{
}
// 如果People继承Animal 则People类和Animal类都会直接提示错误
public class People extends Animal{
}
博主是在以下源码看到的这个用法,jdk虚拟线程相关类:BaseVirtualThread (需要jdk21)
jdk21主要新特性
上面是官方文档提及的,博主简单概述一下:
虚拟线程
-
虚拟线程
这个是最大的新特性了,将操作系统的线程 提到了jvm层面管理,线程开销极大的缩减了,要说怎么理解这个虚拟线程,博主个人把它当做一个操作系统线程的缓存池来理解,智者见智。当然,稍微追踪一下源码很容易发现 它实际原理是通过forkJoinPool来实现的。
(在jdk8引入的completablefuture 默认也是forkJoinPool来实现的)。
Runnable runnable = () -> {
System.out.println("Hello, 孟秋与你");
};
// 方式一:使用静态构建器方法 启动,一个快速的虚拟线程
Thread.startVirtualThread(runnable);
// 方式二:builder 方式启动
Thread.Builder.OfVirtual virtual = Thread.ofVirtual();
// 可以自定义些属性
virtual.name("virtual thread");
virtual.start(runnable);
// 方式三:Executors 也支持了虚拟线程
ExecutorService executorService = Executors.newVirtualThreadPerTaskExecutor();
executorService.execute(runnable);
// 为了确保主线程不会过早退出,等待一段时间
try {
Thread.sleep(1111111000);
} catch (InterruptedException e) {
e.printStackTrace();
}
虚拟线程有没有必要使用线程池 ?
我们知道阿里规范是不允许通过Executors来创建线程的,虚拟线程它成本低 自动销毁 无需复用,但是线程池也有拒绝策略之类的功能存在,硬要用 好像也不是不行 。
总之显得很矛盾,博主个人观点是不要使用线程池。
如果硬用池化,手动创建方式例子如下:
ThreadFactory virtualThreadFactory = Thread.ofVirtual().factory();
ThreadPoolExecutor executor = new ThreadPoolExecutor(
4,
4,
60L,
TimeUnit.SECONDS,
// 注: 这个是无界队列
new LinkedBlockingQueue<>(),
virtualThreadFactory
);
Runnable runnable1 = () -> {
System.out.println("Hello, 孟秋与你1111111");
try {
Thread.sleep(3000L);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
};
for (int i = 0; i < 100; i++) {
executor.execute(runnable1);
}
核心就是线程工厂要设置成虚拟线程工厂,其它参数都是一样的
我们可以看一下源码
-
新的字符串方法
引入了一些新的字符串方法,进一步增强了字符串操作的能力,如字符串格式化和更高效的字符串比较 -
Sequenced Collections
JDK 21增加了有序集合(Sequenced Collections)的支持,这是一个新的集合类型,元素有固定的遇见顺序,并且提供了一组统一的操作。新的接口包括SequencedCollection、SequencedSet和SequencedMap 。 -
ZGC垃圾收集器
-
record 增强
现在基本都用lomhok了 博主没去看这个 -
模式匹配增强(Pattern Matching Enhancements)
模式匹配得到了进一步增强,特别是在switch语句中的应用。这使得代码更加简洁和表达力更强 。 -
性能、安全的优化,方法的弃用之类的优化