JDK8升级后内容

背景

2022 年 Spring 6 和 SpringBoot 3 相继推出。在此之前,Java 社区一直是"新版任你发,我用 Java 8",不管新版本怎么出,很少有人愿意升级。

这一次,Spring 直接来了个大招,Spring 6 和 SpringBoot 3 的最低依赖就是JDK17!跨过 JDK 8 ~ JDK 16,直接升级到 JDK 17。

那么,为什么是 JDK 17 呢?

为什么是 JDK 17

有这么多新版本的 JDK,而且 2022 年还会推出 JDK 18 和 JDK 19,为什么 Spring 选择了 JDK 17 呢?

主要因为它是一个 Oracle 官宣可以免费商用的 LTS 版本。LTS 即 Long Term Support,也就是官方保证会长期支持的版本。

JDK8

新语法

  • Lambda 表达式和函数式接口
  • 接口默认方法和静态方法

核心类库

  • Optional Java 8也将Optional加入了官方库。 解决NullPointerException。
  • Stream流,新增的Stream API(java.util.stream将生成环境的函数式编程引入了Java库中。
  • Date/Time API

Clock 类LocalDate、LocalTime和LocalDateTime

Clock.systemUTC() 等价于System.currentTimeMillis() 和 TimeZone.getDefault()

ZoneDateTime

Duration类

  • Base64 引入官方库 java.util.Base64

JVM

  • 使用 Metaspace (JEP 122)代替持久代(PermGen space)

疑问为何要在jdk8中消除永久代嘞 作者动机是什么?

JEP 122 消除永久代 openjdk.org/jeps/122

动机:这是 JRockit 和 Hotspot 融合工作的一部分。

JRockit 客户不需要配置永久代(因为 JRockit 没有永久代) ,并且习惯于不配置永久代。

JRockit说明:Oracle JRockit (原来的 Bea JRockit)系列产品是一个全面的Java运行时解决方案组合,包括了行业最快的标准Java解决方案。

该调整面临风险:将内部字符串和类静态值移动到 Java 堆可能会导致内存不足异常或 GC 数量的增加。可能需要用户对 -Xmx 进行一些调整。

JDK9

GC

  • 默认GC是使用G1
  • 弃用的垃圾收集器 (GC) 组合
    ParNew + SerialOld
    DefNew + CMS
  • Deprecates 弃用并发标记扫描 (CMS) 垃圾收集器 JEP 291 openjdk.org/jeps/291
    为何要弃用CMS?

(G1)垃圾收集器完全替代 CMS 的大多数用法。

废弃并发标记清除(CMS)垃圾收集器,以便在将来的主要版本中停止对它的支持。

研发者动机和目标:加速 HotSpot 中其他垃圾收集器的开发。减少 GC 代码库的维护负担,并加速新的开发。

核心类库

  • HTTP/2客户端

Java 9 引入了新的 API,它使用起来更干净、更清晰,并且还增加了对 HTTP/2 的支持。

  • Collection(集合) API更新
javascript 复制代码
//在 Java 9 中为集合的创建增加了静态工厂创建方式,也就是 of 方法,通过静态工厂 of 方法创建的集合是只读集合,里面的对象不可改变。
List<String> namesList = List.of("Lokesh", "Amit", "John");
Set<String> namesSet = Set.of("Lokesh", "Amit", "John");
Map<String, String> namesMap = Map.ofEntries(Map.entry("1", "Lokesh"),Map.entry("2", "Amit"),Map.entry("3", "Brian"));
  • Stream(流) API改进

Java 9 引入了两种与 Streams 交互的新方法,即takeWhile/dropWhile方法。

ini 复制代码
	List<String> alphabets = List.of("a", "b", "c", "d", "e", "f", "g", "h", "i");
        List<String> one = alphabets.stream()
                //takeWhile: 从头开始筛选,遇到不满足的就结束了。
                .takeWhile(s -> !s.equals("d"))
                .collect(Collectors.toList());
        //打印出:[a, b, c]
        System.out.println(one);
        List<String> alphabets2 = List.of("a", "b", "c", "d", "e", "f", "g", "h", "i");
        List<String> subset2 = alphabets2.stream()
                .dropWhile(s -> !s.equals("d")) //odropWhile: 从头开始删除,遇到不满足的就结束了。
                .collect(Collectors.toList());
        // 打印出:[d, e, f, g, h, i]
        System.out.println(subset2);
        /*** 此外,它还添加了两个重载方法,即ofNullable和iterate方法,类似option的用法。*/
        //在 Java 8 之前,流中不能有null值。它会导致NullPointerException.
        // 从 Java 9 开始,Stream.ofNullable()方法允许您创建一个单元素流,该流包装一个不为null的值,否则为空流。
        Stream.ofNullable(alphabets2).collect(Collectors.toList());
        /*** 在 Stream 增强之外,还增强了 Optional ,Optional 增加了可以转换成 Stream 的方法。*/
        Stream<Integer> s = Optional.of(1).stream();
        s.forEach(System.out::print);
  • 多版本Jar文件
  • @Deprecated 注解更改
kotlin 复制代码
	/*** 从 Java 9 开始,@Deprecated注解将具有两个属性,即forRemoval和since.
	   * @eprecated  forRemoval -- 指示带注释的元素是否会在未来版本中被删除。
	   *             since - 它返回注释元素被弃用的版本。
	   */
	@Deprecated(forRemoval = true,since = "jdk9")
  • 模块化

JPMS(Java Platform Module System)是Java 9发行版的核心亮点。

JDK 9 附带了大约 92 个模块(在 GA 版本中可以进行更改)。Java 9 Module System有一个"java.base"模块。它被称为基本模块。它是一个独立的模块,不依赖于任何其他模块。默认情况下,所有其他模块都依赖于"java.base"。

个各模块通常只是一个 jar 文件,在根目录下有一个文件module-info.class。

要使用模块,请将 jar 文件包含到modulepath而不是classpath. 添加到类路径的模块化 jar 文件是普通的 jar 文件,module-info.class文件将被忽略。

  • 接口私有方法
csharp 复制代码
	/**
         * Java 8 允许在接口中编写默认方法,这是一个广受欢迎的功能。从 Java 9 开始,你可以在接口中包含私有方法
         * 私有接口方法规则:
         * 私有接口方法不能是抽象的
         * 私有方法只能在接口内部使用。
         * 私有静态方法可以在其他静态和非静态接口方法中使用。
         * 私有非静态方法不能在私有静态方法中使用
         */
        public interface Custom {
            default int addEvenNumbers(int... nums) {
                return add(n -> n % 2 == 0, nums);
            }

            default int addOddNumbers(int... nums) {
                return add(n -> n % 2 != 0, nums);
            }

            // 私有的底层方法
            private int add(IntPredicate predicate, int... nums) {
                return IntStream.of(nums).filter(predicate);
            }
        }

JDK10

描述

  • 自从 Java 9 开始,Oracle 调整了 Java 版本的发布策略,不再是之前的 N 年一个大版本,取而代之的是 6 个月一个小版本,三年一个大版本,这样可以让 Java 的最新改变迅速上线,而小版本的维护周期缩短到下个版本发布之前,大版本的维护周期则是 3 年之久。而 10 就是这么一个小版本,因为 Java 的后续版本基本都会包含之前新特性。

新语法

  • JEP 286 var 局部类型推断

JEP 286 var 局部类型推断

让 Java 可以像 Js 里的 var 或者其他语言的 auto 一样可以自动推断数据类型。
这其实只是一个新的语法糖,底层并没有变化 ,在编译时就已经把 var 转化成具体的数据类型了,但是这样可以减少代码的编写。

让 Java 可以像 Js 里的 var 或者其他语言的 auto 一样可以自动推断数据类型。

ini 复制代码
      	var hashMap = new HashMap<String, String>();
        hashMap.put("a", "b");
        var string = "hello java 10";
        var stream = Stream.of(1, 2, 3, 4);
        var list = new ArrayList<String>();

GC

  • G1 的自动切换并行收集

早在 Java 9 时就已经引入了 G1 垃圾收集器,G1 的优点很多。而在 Java 10 中还是做了小小调整,当G1 的并发收集线程不能快速的完成full GC 时,就会自动切换到并行收集,这可以减少在最坏情况下的 GC 速度。

也可以看出 G1的作者 其实在fullGC 的情况上处理 还在优化,因为他们设计G1的初衷 就是极力避免full GC的出现。但是当并发收集不能足够快地回收内存时,就会引发full GC 垃圾收集。G1的full GC的旧 实现 使用的单线程-标记-清理-压缩(a single threaded-mark-sweep-compact )算法。在 JEP 307中 作者决定采用 并行-清除-压缩(parallelize the mark-sweep-compact ) 算法 ,并使用与 Young 和Mixed 集合 相同数量的线程来进行处理。

线程数量可以通过命令 : -XX:ParallelGCThreads 来控制。(但是这也会影响我们的线程数量)

风险 设定: G1采用并发 收集fullGC的使用资源 肯定要比之前单线程 的时候更耗资源。

官文资料 :bugs.java.com/view_bug.do...

实验性:

  • GraalVM首次引入

这是一个实验性的 JIT 编译器。Java 10 中最具有未来感的引入。

Graal 其实在 Java 9 中就已经引入了,它带来了 Java 中的 AOT (Ahead Of Time 提前编译)编译,还支持多种语言,如 Js、Python、Ruby、R、以及其他基于 JVM (如 Java、Kotlin)的和基于 LLVM (如 C、C++)的语言。

基本上由Java语言编写。

核心类库

  • Collection新增方法 Collection.copyOf 复制得到一个不可改变集合
  • Optional新增方法 orElseThrow 方法 value = null 直接抛异常。它是现有 get 方法的同义词,是现有 get 方法的首选替代方法。
  • Stream 新增方法 转成不可变集合方法。
scss 复制代码
list.stream().collect(Collectors.toUnmodifiableList());

runtime

  • 字节码生成已经得到改进,增强了 For 循环

在 for 循环之外声明迭代器变量可以在不再使用它时立即为其赋值 null。 这使得 GC 可以访问它,然后 GC 可以处理掉未使用的内存。当增强的 for 循环中的表达式是一个数组时,也会执行类似的操作。
衍生问题 研发者为何要在for循环后做该编译优化操作?

个人理解 是为了配合GC ,GC中的 Safe Point 安全点的设置。

安全点位置的选取基本上是以 "是否具有让程序长时间执行的特征" 为标准 进行选定的,因为每条指令执行的时间都非常短暂,程序不太可能因为指令流长度太长这样的原因而 长时间执行, "长时间执行" 的最明显特征就是指令序列的复用,例如方法调用、循环跳转、异常跳转 等都属于指令序列复用,所以只有具有这些功能的指令才会产生安全点。

只有到达某些点才可以进行GC操作,这些点称作安全点(Safepoint),比如,循环的末尾、方法临返回前/调用方法的call指令后、可能跑异常的位置等。
安全点:在OopMap的协助下,HotSpot可以快速准确地完成GC Roots枚举,但随之而来的一个现实问题:可能导致引用关系变化,或者说导致OopMap内容变化的指令非常之多,如果为每一条指令都生成对应的OopMap,那将会需要大量的额外存储空间,这样垃圾收集伴随而来的空间成本就会非常高昂。

在实际上,也的确没有为每条指令都生成OopMap,只是在"特定的位置"记录 了这些信息,这些位置被称为安全点(Safepoint)。

有了安全点的设定,也就决定了用户程序执行时 并非在代码指令流的任意位置都能够停顿下来开始垃圾收集,而是强制要求必须执行到达安全点后才能够暂停。

  • 类数据共享

JVM 启动时有一步是需要在内存中加载类,而如果有多个 jar,加载第一个 jar 的速度是最慢的。这就延长了程序的启动时间,为了减少这个时间,Java 10 引入了应用程序类数据共享(CDS)机制,它可以把你想共享的类共享在程序之间,使不同的 Java 进程之间共享这个类来减少这个类占用的空间以及加载速度。

JDK11

描述

  • Java 11 是 Java 8 之后的第一个 LTS(长期支持) 版本。
    核心类库
  • String API的改动
rust 复制代码
        /*** =================String 新增api功能========================================*/
        String str= "aa\nbb";
        // 判空,blank
        System.out.println(str.isBlank());
        // 该方法是根据 换行符 \n  或者回车  \r 或者回车换行  \r\n  进行分割
        Stream<String> lines = str.lines();
        lines.forEach(System.out::println);
        // 复制字符串
        String str1= "abc";
        String repeat = str1.repeat(2);
        System.out.println(repeat);/*** 输出* false* aa* bb* abcabc*/
        // 去除前后空白
        String strip = "     string字符  ";
        System.out.println("==" + strip.trim() + "==");
        // 去除前后空白字符,如全角空格,TAB
        System.out.println("==" + strip.strip() + "==");
        // 去前面空白字符,如全角空格,TAB
        System.out.println("==" + strip.stripLeading() + "==");
        // 去后面空白字符,如全角空格,TAB
        System.out.println("==" + strip.stripTrailing() + "==");
        // 输出
        // ==  string字符  ==
        // ==string字符==
        // ==string字符  ==
        // ==     string字符==
        /**这里注意,trim 只能去除半角空格,而 strip 是去除各种空白符。*/
  • File API改动
ini 复制代码
        /**=================* File API改动* 读写文件变得更加方便。* ========================================*/
        // 创建临时文件
        Path path = Files.writeString(Files.createTempFile("test", ".txt"), "https://www.baidu.com");
        System.out.println(path);
        // 读取文件
        String ss = Files.readString(Path.of("file.json"));
        String s = Files.readString(path);
        System.out.println(s);
  • HTTP Client
scss 复制代码
 	 /**=================* HTTP Client* 在 Java 11 中 Http Client API 得到了标准化的支持。且支持 HTTP/1.1 和 HTTP/2 ,
         * 也支持 websockets。* http://openjdk.java.net/groups/net/httpclient/recipes-incubating.html
         * HTTPClient 已经在 Java11中标准化了。
         * * ========================================*/
        HttpClient client = HttpClient.newHttpClient();
        HttpRequest request = HttpRequest.newBuilder().uri(URI.create("https://www.weibo.com")).build();
        // 异步
        client.sendAsync(request, HttpResponse.BodyHandlers.ofString()).thenApply(HttpResponse::body).thenAccept(System.out::println).join();
        // 同步
        HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
        System.out.println(response.body());

新语法

  • Lambda 参数的局部变量语法
css 复制代码
  	 /**=================** Lambda 局部变量推断
         * 集合jdk10引入的var语法,在jdk11的时候,这个语法糖可以在Lambda中进行使用。
         * ** ========================================*/
        var hashMap = new HashMap<String, Object>();
        hashMap.put("A", "a");
        hashMap.put("B", "b");
        // 这里需要注意的是,(var k,var v) 中,k 和 v 的类型要么都用 var ,要么都不写,要么都写正确的变量类型。而不能 var 和其他变量类型混用。
        hashMap.forEach((var k, var v) -> {
            System.out.println(k + ": " + v);
        }});

runtime

  • 单命令运行 Java
ruby 复制代码
        /***  单命令运行 Java*  之前运行一个java 程序
         *  1. javac 编译字节码
         *  2. java 运行程序class文件
         *  jdk11之后
         *  $ java  xxx.java   即可运行
         *  */

实验性

  • 可伸缩的低延迟垃圾收集器 - 引入ZGC垃圾收集器 jdk11提出而已 仅支持在 Linux/x64上使用。
  • A No-Op (理解成无操作)垃圾收集器 -Epsilon GC
    Hotspot 编译器
  • 编译器线程的延迟分配

在默认打开的分层编译模式下,VM 在具有许多 CPU 的系统上启动大量编译器线程,而不管可用内存和编译请求的数量如何。因为线程即使在空闲时(几乎总是空闲)也会消耗内存,这导致资源的使用效率低下。

为了解决这个问题,实现进行了更改,以便在启动期间只启动每种类型的一个编译器线程,并动态处理进一步线程的启动和关闭。它由一个新的命令行标志控制,默认情况下是打开的。

JDK12

核心类库

  • 文件对比 Files.mismatch
ini 复制代码
        /*** 文件对比 Files.mismatch* 对比两个文件内容是否一致,如果内容一致,会返回 -1  ,如果内容不同,会返回不同的字节开始位置。*/
        // 创建两个临时文件
         Path aFile = Files.createFile(Paths.get("A.txt"));
         Path bFile = Files.createFile(Paths.get("B.txt"));
         // 写入相同内容
        Files.write(aFile,"123456".getBytes(), StandardOpenOption.WRITE);
        Files.write(bFile,"123456".getBytes(), StandardOpenOption.WRITE);
        long mismatch = Files.mismatch(aFile, bFile);
        System.out.println(mismatch);
        // 追加不同内容
         Files.write(aFile,"789".getBytes(), StandardOpenOption.APPEND);
         Files.write(bFile,"987".getBytes(), StandardOpenOption.APPEND);
         mismatch = Files.mismatch(aFile, bFile);
         System.out.println(mismatch);
         // 删除创建的文件
         aFile.toFile().deleteOnExit();
         bFile.toFile().deleteOnExit();
         //输出//-1  相同//6   6下标  从0开始的话的 也就是第七位开始不同 正好是上面追加的点
  • 数据格式处理NumberFormat
ini 复制代码
        /**
         * 紧凑数字格式化的支持 NumberFormat 添加了对紧凑形式的数字格式化的支持。紧凑型数字格式是指以简短或人类可读的形式表示数字。
         * 例如,在 en _ US 区域设置中,根据 NumberFormat 指定的样式,
         * 1000可以格式化为"1K",1000000可以格式化为"1M"。风格。
         * 紧凑数字格式由 LDML 的紧凑数字格式规范定义
         *  */
        System.out.println("Compact Formatting is:");
        NumberFormat upvotes = NumberFormat.getCompactNumberInstance(new Locale("en", "US"), NumberFormat.Style.SHORT);
        System.out.println(upvotes.format(100));
        System.out.println(upvotes.format(1000));
        System.out.println(upvotes.format(10000));
        System.out.println(upvotes.format(100000));
        System.out.println(upvotes.format(1000000));
        // 设置小数位数
        upvotes.setMaximumFractionDigits(1);
        System.out.println(upvotes.format(1234));
        System.out.println(upvotes.format(123456));
        System.out.println(upvotes.format(12345678));
        /**
         * Compact Formatting is:
         * 100
         * 1K
         * 10K
         * 100K
         * 1M
         * 1.2K
         * 123.5K
         * 12.3M
         */

GC

  • ZGC现在支持类卸载。

通过卸载未使用的类,可以释放与这些类相关的数据结构,从而降低应用程序的总体占用。

ZGC 中的类卸载同时进行,而不会停止 Java 应用程序线程的执行,因此对 GC 暂停时间没有影响。

默认情况下启用此特性,但可以使用命令行选项 -XX:-ClassUnload 禁用此特性。

预览功能

  • Switch 表达式 改进 case "A", "B", "C" -> "method"; 的形式,让Switch的代码编写变得更加优雅。
    当case穿透开始时,后续的case就会失去匹配效果,内部的语句都会被执行,直到遇到break语句跳出switch,或者将整体switch语句执行完毕,才会结束。

jdk12之前版本

ini 复制代码
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入星期几:");
        int week = sc.nextInt();
        String str = "";
        switch (week) {
            case 1:
            case 2:
            case 3:
            case 4:
            case 5:
                str = "工作";
                break;
            case 6:
            case 7:
                str = "休息";
                break;
            default:
                str = "输入有误";
                break;
        }
        System.out.println(str);
        /**
         * 请输入星期几:
         * 2
         * 工作
         */

jdk12之后

ini 复制代码
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入星期几:");
        int week = sc.nextInt();
        String str = switch (week) {
            case 1, 2, 3, 4, 5 -> "工作";
            case 6, 7 -> "休息";
            default -> "输入有误";
        };
        System.out.println(str);
        /**
         * 请输入星期几:
         * 7
         * 休息
         */

删除项

  • finalize 方法的删除

从 FileInputStream 和 FileOutputStream 中删除 finalize 方法, finalize 方法在 JDK9中不被推荐删除。jdk12中finalize方法被正式移除 。

删除 java.util 中的 finalize 方法。

未完待续!

相关推荐
MacroZheng15 分钟前
完美替代 Navicat!这款内置 AI 的数据库工具,太香了!
java·后端·mysql
SamDeepThinking41 分钟前
从REST到gRPC,一个API选型的思考框架
java·后端·程序员
Zane199444 分钟前
接口都能写默认实现了,为什么还需要抽象类
java·后端
未秃头的程序猿1 小时前
读Spring AI源码的方法——不是硬啃,是带着问题去翻
java·后端·spring
用户3126874877201 小时前
别再 Thread.sleep 硬等了!并发工具类到底怎么选?
java
Java内核笔记1 小时前
万字长文剖析 Spring Boot 4 自动配置机制源码:从 @EnableAutoConfiguration 到条件装配
java·后端
evans在进步1 小时前
Spring MVC 核心机制详解:全局异常处理、请求跳转与数据绑定
java·spring·mvc
weixin_BYSJ19871 小时前
springboot小区物业管理小程序---附源码45555
java·javascript·spring boot·python·django·flask·php
coder_lorraine1 小时前
Halo 2.x 回收站文章无法删除?一篇文章教你彻底解决
java·运维
索隆zoro2 小时前
Army 进阶:DML 操作体系与 Spring AI 集成
java