侧边栏壁纸
  • 累计撰写 249 篇文章
  • 累计创建 138 个标签
  • 累计收到 16 条评论

目 录CONTENT

文章目录

JDK8+ 新特性

Sherlock
2021-09-16 / 0 评论 / 0 点赞 / 855 阅读 / 45674 字 / 编辑
温馨提示:
本文最后更新于 2023-10-11,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

前言

JDK LTS 版每 2 年一个版本(原 3 年)
非 LTS 版每半年一个版本

JDK  8 2014-03 LTS 版本
JDK 11 2018-09 LTS 版本
JDK 12 2019-03 non-LTS 功能性版本
JDK 13 2019-09 non-LTS 功能性版本
JDK 14 2020-03 non-LTS 功能性版本
JDK 15 2020-09 non-LTS 功能性版本
JDK 16 2021-03 non-LTS 功能性版本
JDK 17 2021-09-15 LTS
JDK 18 2022-03 non-LTS 功能性版本
JDK 19 2022-09 non-LTS 功能性版本
JDK 20 2023-03 non-LTS 功能性版本
JDK 21 2023-09 LTS

Java 17+ 可以免费使用了,包括商用,更详细的条款可以阅读:
https://www.oracle.com/downloads/licenses/no-fee-license.html

JDK 17 是自 2018 年 JDK 11 后的第二个长期支持版本,支持到 2029 年 9 月,好家伙,支持时间长达 8 年,这下可以不用死守 JDK 8 了,JDK 17+ 也可以是一种新的选择了。。

下一个第三个长期支持版本是 JDK 21,时间为 2023 年 9 月,这次长期支持版本发布计划改了,不再是原来的 3 年一次,而是改成了 2 年一次!

另外,非长期支持版本还是半年发一次不变,下一个非长期支持版本计划在 2022/03 发布,但注意不要用在生产。

JDK 17 下载地址:

https://download.oracle.com/java/17/latest/jdk-17_windows-x64_bin.zip
https://download.oracle.com/java/17/latest/jdk-17_linux-x64_bin.tar.gz

OpenJDK 17 发布地址:
http://openjdk.java.net/projects/jdk/17/
OpenJDK 17 下载地址:
https://jdk.java.net/17/

JEP

JDK Enhancement Process

Oracle发布了JDK增强提案与路线图进程,JEP的目的在于根据某个特性来定义所需的增强或是修改。
鼓励OpenJDK提交者贡献点子和扩展以改进OpenJDK生态圈。

http://openjdk.java.net/jeps/0

进程文档明确指出 JEPs 并不会取代 Java Community Process;
因为 JCP 是标准 Java SE APIs 与相关接口的管理部门。

JEPs会经历各种状态转换,如下所示:

  • 草案:开放讨论
  • 张贴:进入JEP归档
  • 提交:开始评估
  • 活动:批准公开发布
  • 候选:获准进入OpenJDK路线图
  • 资助:由小组/区域领导判断给予全力资助
  • 完成:完成与交付
  • 撤回:退出(或许未来还会重新加入进来)
  • 拒绝:现在或将来不值得继续

JDK9 集合加强

Jdk 9 里面为集合(List/ Set/ Map)都添加了 ofcopyOf 方法,它们两个都用来创建不可变的集合

示例1:

List list = List.of("Java", "Python", "C");
List copy = List.copyOf(list);
System.out.println(list == copy);  // true

示例2:

List list = new ArrayList<String>();
List copy = List.copyOf(list);
System.out.println(list == copy);  // false

源码:

static <E> List<E> of(E... elements) {
    switch (elements.length) { // implicit null check of elements
        case 0:
            return ImmutableCollections.emptyList();
        case 1:
            return new ImmutableCollections.List12<>(elements[0]);
        case 2:
            return new ImmutableCollections.List12<>(elements[0], elements[1]);
        default:
            return new ImmutableCollections.ListN<>(elements);
    }
}

static <E> List<E> copyOf(Collection<? extends E> coll) {
    return ImmutableCollections.listCopy(coll);
}

static <E> List<E> listCopy(Collection<? extends E> coll) {
    if (coll instanceof AbstractImmutableList && coll.getClass() != SubList.class) {
        return (List<E>)coll;
    } else {
        return (List<E>)List.of(coll.toArray());
    }
}

可以看出 copyOf 方法会先判断来源集合是不是 AbstractImmutableList 类型的,如果是,就直接返回,如果不是,则调用 of 创建一个新的集合。

示例2因为用的 new 创建的集合,不属于不可变 AbstractImmutableList 类的子类,所以 copyOf 方法又创建了一个新的实例,所以为false.

注意:使用 of 和 copyOf 创建的集合为不可变集合,不能进行添加、删除、替换、排序等操作,不然会报 java.lang.UnsupportedOperationException 异常。

JDK9 Stream 加强

    1. 增加单个参数构造方法,可为null
Stream.ofNullable(null).count(); // 0
    1. 增加 takeWhile 和 dropWhile 方法
Stream.of(1, 2, 3, 2, 1)
    .takeWhile(n -> n < 3)
    .collect(Collectors.toList());  // [1, 2]

从开始计算,当 n < 3 时就截止。

Stream.of(1, 2, 3, 2, 1)
    .dropWhile(n -> n < 3)
    .collect(Collectors.toList());  // [3, 2, 1]

这个和上面的相反,一旦 n < 3 不成立就开始计算。

  • 3)iterate重载
    这个 iterate 方法的新重载方法,可以让你提供一个 Predicate (判断条件)来指定什么时候结束迭代。

Optional 加强

Opthonal 也增加了几个非常酷的方法,现在可以很方便的将一个 Optional 转换成一个 Stream, 或者当一个空 Optional 时给它一个替代的。

Optional.of("javastack").orElseThrow();     // javastack
Optional.of("javastack").stream().count();  // 1
Optional.ofNullable(null)
    .or(() -> Optional.of("javastack"))
    .get();   // javastack

InputStream 加强

InputStream 终于有了一个非常有用的方法:transferTo,可以用来将数据直接传输到 IutputStream,这是在处理原始数据流时非常常见的一种用法,如下示例。

var classLoader = ClassLoader.getSystemClassLoader();
var inputStream = classLoader.getResourceAsStream("javastack.txt");
var javastack = File.createTempFile("javastack2", "txt");
try (var outputStream = new FileOutputStream(javastack)) {
    inputStream.transferTo(outputStream);
}

JDK10 局部变量类型推断

1、字面量定义局部变量

private static void testVar() {    
	var javastack = "javastack";    
	System.out.println(javastack);
}

2、接收方法返回值定义局部变量

private static void testMethod() {    
    var javastack = getJavastack();    
    System.out.println(javastack);
}

public static String getJavastack() {    
    return "javastack";
}

3、循环中定义局部变量

private static void testLoop() {    
    for (var i = 0; i < 3; i++) {        
        for (var m = 10; m < 15; m++) {            
            System.out.println(i + m);        
        }    
    }
}

4、泛型结合局部变量

private static void testGeneric() {    
    // 表达式1    
    List<String> list1 = new ArrayList<>();    
    list1.add("javastack");    
    // 表达式2    
    var list2 = new ArrayList<>();    
    list2.add(2018);    
    // 表达式3    
    var list3 = new ArrayList<String>();    
    list3.add("javastack");
}

表达式1后面 <> 里面 jdk 1.7+开始是不用带具体类型的,在接口中指明就行了。
表达式2中如果使用 var 的话, <> 里面默认会是 Object 的,所以可以添加任意类型。
表达式3中在 <> 强制使用了 String 来指定泛型。

局部变量类型推断不能用在以下场景

  • 1、类成员变量类型
  • 2、方法返回类型
  • 3、Lambda 表达式

优点:简化代码

缺点:掩盖类型

var 关键字原理

var其实就是 Java 10 增加的一种语法糖而已,在编译期间会自动推断实际类型,其编译后的字节码和实际类型一致,如以下例子所示。

private static void testByteCode() {    
	String javastack1 = "javastack";    
	var javastack2 = "javastack";
}

编译成字节码后:

private static testByteCode()V
L0
LINENUMBER 22 L0
LDC "javastack"
ASTORE 0
L1
LINENUMBER 23 L1
LDC "javastack"
ASTORE 1
L2
LINENUMBER 24 L2
RETURN
L3
LOCALVARIABLE javastack1 Ljava/lang/String; L1 L3 0
LOCALVARIABLE javastack2 Ljava/lang/String; L2 L3 1
MAXSTACK = 1
MAXLOCALS = 2

可以看出 javastack1javastack2 都是虚拟机所认识的的本地变量类型: java.lang.String,虚拟机并不认识 var, 所以 var 并不神奇。

JDK11 字符串加强

// 判断字符串是否为空白
" ".isBlank(); // true

// 去除首尾空格
" Javastack ".strip(); // "Javastack"

// 去除尾部空格 
" Javastack ".stripTrailing(); // " Javastack"

// 去除首部空格 
" Javastack ".stripLeading(); // "Javastack "

/**************** repeat ******************/
// 复制字符串
"Java".repeat(3); // "JavaJavaJava"

// 小于0:java.lang.IllegalArgumentException
str.repeat(-2);

// 等于0:空白串("")
str.repeat(0);

// java.lang.OutOfMemoryError
// 并不是可以无限增长的,有使用限制的,达到一定量就会报内存溢出异常
str.repeat(Integer.MAX_VALUE);

/**************** lines ******************/
// 方法返回一个字符串 Stream, 可以识别 \n 和 \r 换行符换行
public Stream<String> lines() {
    return isLatin1() ? StringLatin1.lines(value)
                      : StringUTF16.lines(value);
}

// 行数统计
"A\nB\nC".lines().count(); // 3
// 如批量读取文件内容到一个 Stream 中,就能很好的识别行结束符了

JDK11 快速运行源代码

/*旧方法*/
// 编译
javac Javastack.java
// 运行
java Javastack
    
/*新方法*/
java Javastack.java

JDK12 String 字符串骚操作

1.transform

transform:即字符串转换,源码:

public <R> R transform(Function<? super String, ? extends R> f) {
    return f.apply(this);
}
private static void testTransform() {
    System.out.println("======test java 12 transform======");
    List<String> list1 = List.of("Java", " Python", " C++ ");
    List<String> list2 = new ArrayList<>();

    list1.forEach(element ->
            list2.add(element.transform(String::strip)
                    .transform(String::toUpperCase)
                    .transform((e) -> "Hi," + e))
    );

    list2.forEach(System.out::println);
    /**输出:
    ======test java 12 transform======
    Hi,JAVA
    Hi,PYTHON
    Hi,C++
    */
}

2.indent

private static void testIndent() {
    System.out.println("======test java 12 indent======");
    // 换行符 \n 后前缩进 N 个空格,为 0 或负数不缩进。
    String result = "Java\n Python\nC++".indent(3);
    System.out.println(result);
}

indent 的核心源码:

private String indent(int n, boolean removeBlanks) {
    Stream<String> stream = removeBlanks ? lines(Integer.MAX_VALUE, Integer.MAX_VALUE)
                                         : lines();
    if (n > 0) {
        final String spaces = " ".repeat(n);
        stream = stream.map(s -> spaces + s);
    } else if (n == Integer.MIN_VALUE) {
        stream = stream.map(s -> s.stripLeading());
    } else if (n < 0) {
        stream = stream.map(s -> s.substring(Math.min(-n, s.indexOfNonWhitespace())));
    }
    return stream.collect(Collectors.joining("\n", "", "\n"));
}

其实就是调用了 lines() 方法来创建一个 Stream,然后再往前拼接指定数量的空格。

3.describeConstable

private static void testDescribeConstable() {
    System.out.println("======test java 12 describeConstable======");
    String name = "Java爱好者";
    Optional<String> optional = name.describeConstable();
    System.out.println(optional.get());
}

结果输出:

======test java 12 describeConstable======
Java爱好者

Java 12, String 实现了 Constable 接口:

java.lang.constant.Constable
这个接口就有一个方法,源码如下:

public interface Constable {
    Optional<? extends ConstantDesc> describeConstable();
}

Java 12 String 的实现源码:

@Override
public Optional<String> describeConstable() {
    return Optional.of(this);
}

很简单,其实就是调用 Optional.of 方法返回一个 Optional 类型

JDK13 文本块

Java 13 之前:

String html = "<html>\n" +
              "    <body>\n" +
              "        <p>Hi, Java技术栈</p>\n" +
              "        <p>欢迎关注,分享更多干货</p>\n" +
              "    </body>\n" +
              "</html>\n";

Java 13+:

String html = """
              <html>
                  <body>
                      <p>Hi, Java技术栈</p>
                      <p>欢迎关注,分享更多干货</p>
                  </body>
              </html>
              """;

JDK14 instanceof 模式匹配

老代码写法:

if (object instanceof Kid) {
    Kid kid = (Kid) object;
    // ...
} else if (object instanceof Kiddle) {
    Kid kid = (Kid) object;
    // ...
}

新代码写法:

if (object instanceof Kid kid) {
    // ...
} else if (object instanceof Kiddle kiddle) {
    // ...
}

JDK14 Records 代替 Lombok?

不能完全代替!

public record Student(String name, int id, int age) {}

Records 类有以下限制:

  • 1)record 类是 final 修饰的,所以不能被其他子类继承;
  • 2)因为 Java 类是单继承,而自身又已经继承了 Record 类,所以不能再继承其他类,但是可以实现接口;
  • 3)成员变量也是 final 类型的,所以其值或者引用不能被更改,如果是引用类型,可以修改对象里面的值。

JDK14 空指针异常详细信息

官方介绍 https://openjdk.java.net/jeps/358

javastack.name = params.user.name;
----------------------------------
Exception in thread "main" java.lang.NullPointerException: 
        Cannot read field "name" because "params.user" is null
    at Test.main(Test.java:3)

a[i][j][k] = 2020;
----------------------------------
Exception in thread "main" java.lang.NullPointerException:
        Cannot load from object array because "a[i][j]" is null
    at Test.main(Test.java:18)            

在同一行上显示异常类型、异常消息会导致行很长,所以为了保持可读性,会在第二行显示详细异常信息。

这个功能在 Java 14 默认情况下是不开启的,可以使用以下 JVM 参数进行切换:
开启:-XX:+ShowCodeDetailsInExceptionMessages
关闭:-XX:-ShowCodeDetailsInExceptionMessages

为什么现在默认不开启?

1)性能
如果应用程序频繁地抛出并打印异常堆栈消息,势必会带来一定的开销、影响性能,所以应尽量避免这种开销。

2)安全
这个会导致更多源代码的暴露,如果这个不能接受,则不应由 JVM 配置应用程序打印,而应捕获并丢弃。

3)兼容性
过去的 JDK 都是不打印详细空指针异常信息的,JVM 相关工具要依赖于异常消息的准确格式,有可能会存在兼容性问题。

所以,这个特性暂时默认是关闭的,在未来不久的版本中会默认开启。

JDK15 特性介绍

  • 371:Hidden Classes

隐藏类,这一看也是个很有意思的特性。

隐藏类是为框架(frameworks)所设计的,隐藏类不能直接被其他类的字节码使用,只能在运行时生成类并通过反射间接使用它们。

  • 372:Remove the Nashorn JavaScript Engine

移除了 Nashorn JavaScript 脚本引擎、APIs,以及 jjs 工具。这些早在 JDK 11 中就已经被标记为 deprecated 了,JDK 15 被移除就很正常了。

  • 374:Disable and Deprecate Biased Locking

准备禁用和废除偏向锁,在 JDK 15 中,默认情况下禁用偏向锁,并弃用所有相关的命令行选项。

后面再确定是否需要继续支持偏向锁,国为维护这种锁同步优化的成本太高了。

  • 377:ZGC: A Scalable Low-Latency Garbage Collector

ZGC:一个可伸缩、低延迟的垃圾回收器。

ZGC 最早是在 JDK 11 中集成进来的,JDK 15 只是将 ZGC 垃圾收集器从预览特性变更为正式特性而已,没错,转正了。

这个 JEP 不会更改默认的 GC,默认仍然是 G1

  • 378:Text Blocks

文本块,是一个多行字符串,它可以避免使用大多数转义符号,自动以可预测的方式格式化字符串,并让开发人员在需要时可以控制格式。

文本块最早准备在 JDK 12 添加的,但最终撤消了,然后在 JDK 13 中作为预览特性进行了添加,然后又在 JDK 14 中再次预览,在 JDK 15 中,文本块终于转正,暂不再做进一步的更改。

  • 379:Shenandoah: A Low-Pause-Time Garbage Collector

Shenandoah:一个低停顿时间的垃圾回收器。

Shenandoah 最早是在 JDK 12 中集成进来的,JDK 15 只是将 Shenandoah 垃圾收集器从预览特性变更为正式特性而已,没错,又是转正了。

  • 383:Foreign-Memory Access API (Second Incubator)

外存访问 API(二次孵化),可以允许 Java 应用程序安全有效地访问 Java 堆之外的外部内存。

这个最早在 JDK 14 中成为孵化特性,JDK 15 继续二次孵化并对其 API 有了一些更新。

  • 384:Records (Second Preview)

Records 最早在 JDK 14 中成为预览特性,JDK 15 继续二次预览。

Records 在某些场合可以干掉 Lombok 的存在,能自动生成了类构造器、toString()、hashCode()、equals(),以及类似 getter 的变量访问方法。

JDK17 switch 模式匹配(预览中)

老代码写法:

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;
}

新代码写法:

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();
    };
}

JDK 17 Sealed Classes 密封类

JDK 15 中首次成为预览特性,在 JDK 16 中进行二次预览,在 JDK 17 中终于正式转正

public abstract sealed class Student
    permits ZhangSan, LiSi, ZhaoLiu {
    ...
        
}

密封类可以用来增强 Java 编程语言,防止其他类或接口扩展或实现它们。

类 Student 被 sealed 修饰,说明它是一个密封类,并且只允许指定的 3 个子类继承

Oracle JDK VS OpenJDK

有的,虽然两者很接近,但也还是有一点区别!

  • 1、Oracle JDK 提供了各种安装程序,还包含更新规则,而 OpenJDK 只提供了一个纯压缩包;
  • 2、Usage Logging 仅在 Oracle JDK 中可用;
  • 3、Oracle JDK 要求第三方加密提供程序使用 Java 加密扩展(JCE)进行签名,而 OpenJDK 继续允许使用未签名的第三方加密提供程序;
  • 4、java -version 的输出也是不同的,Oracle JDK 返回 java 并包含 Oracle 特定的标识符,OpenJDK 返回 OpenJDK 并且不包含特定于 Oracle 的标识符;
  • 5、许可证不同,Oracle JDK 17+ 是根据 Oracle 免费条款和条件许可发布的,而 OpenJDK 在 GPLv2wCP 下发布的;
  • 6、Oracle JDK 源代码含有 “ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.”,其使用受许可条款约束的,而 OpenJDK 源代码可参考 GPL 许可条款;

后记

0
  1. 支付宝打赏

    qrcode alipay
  2. 微信打赏

    qrcode weixin

评论区