JDK8升级后内容

2023年 10月 8日 131.5k 0

背景

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

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

	List alphabets = List.of("a", "b", "c", "d", "e", "f", "g", "h", "i");
        List one = alphabets.stream()
                //takeWhile: 从头开始筛选,遇到不满足的就结束了。
                .takeWhile(s -> !s.equals("d"))
                .collect(Collectors.toList());
        //打印出:[a, b, c]
        System.out.println(one);
        List alphabets2 = List.of("a", "b", "c", "d", "e", "f", "g", "h", "i");
        List 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 s = Optional.of(1).stream();
        s.forEach(System.out::print);
  • 多版本Jar文件
  • @Deprecated 注解更改
	/*** 从 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文件将被忽略。

  • 接口私有方法
	/**
         * 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 一样可以自动推断数据类型。

      	var hashMap = new HashMap();
        hashMap.put("a", "b");
        var string = "hello java 10";
        var stream = Stream.of(1, 2, 3, 4);
        var list = new ArrayList();

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 新增方法 转成不可变集合方法。
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的改动
        /*** =================String 新增api功能========================================*/
        String str= "aa\nbb";
        // 判空,blank
        System.out.println(str.isBlank());
        // 该方法是根据 换行符 \n  或者回车  \r 或者回车换行  \r\n  进行分割
        Stream 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改动
        /**=================* 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
 	 /**=================* 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 response = client.send(request, HttpResponse.BodyHandlers.ofString());
        System.out.println(response.body());

新语法

  • Lambda 参数的局部变量语法
  	 /**=================** Lambda 局部变量推断
         * 集合jdk10引入的var语法,在jdk11的时候,这个语法糖可以在Lambda中进行使用。
         * ** ========================================*/
        var hashMap = new HashMap();
        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
        /***  单命令运行 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
        /*** 文件对比 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
        /**
         * 紧凑数字格式化的支持 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之前版本

        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之后

        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 方法。

未完待续!

相关文章

服务器端口转发,带你了解服务器端口转发
服务器开放端口,服务器开放端口的步骤
产品推荐:7月受欢迎AI容器镜像来了,有Qwen系列大模型镜像
如何使用 WinGet 下载 Microsoft Store 应用
百度搜索:蓝易云 – 熟悉ubuntu apt-get命令详解
百度搜索:蓝易云 – 域名解析成功但ping不通解决方案

发布评论