自学内容网 自学内容网

Java8 到 Java23 的新特性,都在这里了!

大家好,我是君哥。

虽然多数人还是使用 Java 8,但并没有阻挡 Java 更新的脚步,最近 Java 23 发布了。今天来聊一聊 Java 8 到 java 23 增加了哪些新特性。

Java 9

  • 私有接口方法

  • 默认垃圾收集器改为 G1

  • HTTP client,支持 WebSocket、HTTP/2、HTTPS/TLS、非阻塞 API

Java 10

  • 局部变量类型推断,可以使用 var 类型来定义变量

  • 不可变集合

  • G1 支持并行 Full GC

  • 基于 Java 的 JIT 编译器 Graal

  • 支持在不执行全局安全点的情况下执行线程回调,这样可以在不停止所有线程的情况下停止单个线程

Java 11

  • 标准 HTTP Client 升级

  • 引入 ZGC 垃圾收集器

  • Flight Recorder,可以收集基于 OS、JVM和JDK 事件产生的数据

  • 对Stream、Optional、集合 API进行增强

Java 12

  • 引入 Switch 表达式

  • Shenandoah GC 垃圾收集算法

  • JMH 基准测试

  • G1 支持可中断的 mixed GC,将 Mixed GC 拆分为强制部分和可选部分,强制部分一定会被回收,可选部分可以不被回收,这样垃圾收集过程中优先处理强制集,更容易满足暂停时间目标。

  • G1 可以归还不使用的内存给操作系统

Java 13

  • switch 优化更新,增加 yield 关键字用于返回结果

  • ZGC 支持将未使用的内存归还操作系统

  • 引入了文本块,可以使用 """ 三个引号表示文本块,示例代码如下:

String html = """
  <html>
   <body>
    <p>Hello, world</p>
   </body>
  </html>
""";

Java 14

  • instanceof 语法简化,可以直接给对象赋值:

if (obj instanceof String s) { 
    //这里可以使用 s 变量
} else {
    //这里不能使用 s 变量
}
  • 引入 Record,类似于枚举类型,具有 Lombok 功能,可以自动生成构造器、equals、getter 等方法

  • 放弃 CMS

Java 15

  • 引入 hidden class

  • String.substring 优化,如果长度为 0,返回 null

  • 引入 Sealed class

Java 16

  • Stream新增toList方法

  • 提供jpackage

  • java.time 根据时段获取时间

Java 17

  • 升级 switch 使用,switch可直接用 instanceof 模式匹配选择,不过需要提前做 null 判断(下面代码选自 oschina):

Object o;
switch (o) {
    case null      -> System.out.println("首先判断对象是否为空,走空指针逻辑等后续逻辑");
    case String s  -> System.out.println("判断是否为字符串,s:" + s);
    case record p  -> System.out.println("判断是否为Record类型: " + p.toString());
    case int[] arr -> System.out.println("判断是否为数组,展示int数组的长度" + ia.length);
    case Integer i -> System.out.println("判断是否为Intger对象,i:" + i);
    case Student s   -> System.out.println("判断是否为具体学生对象,student:" + s.toString());
    case UserCommonService -> System.out.println("判断是否为普通用户实现类,然后走普通用户逻辑");
    case UserVipService    -> System.out.println("判断是否为vip用户实现类,然后走vip用户逻辑");
    default   -> System.out.println("Something else");
}
  • 默认启用 Parallel GC

  • 增强TreeMap

  • 统一日志异步刷新,先将日志写入缓存,独立线程负责刷新到相应输出

Java 18

  • Java API 标准库中的字符编码默认为 UTF-8,也就是说我们写代码在处理字符时,不再需要显示指定 UTF-8 编码。

  • 提供了一个命令行工具来启动建议的 Web Server,它是一个文件服务。

  • 支持在 Java API 文档中加入代码片段,如下面代码:

/**
 * The following code shows how to use {@code Optional.isPresent}:
 * {@snippet :
 * if (v.isPresent()) {
 *     System.out.println("v: " + v.get());
 * }
 * }
 */
  • 基于方法句柄重新实现的 java.lang.reflect 作为平台通用的反射底层实现机制,以替代基于字节码生成机制的 Method::invoke, Constructor::newInstance, Field::get 和 Field::set。

  • 引入向量 API,如下面代码:

//不使用向量 API 的写法:
void scalarComputation(float[] a, float[] b, float[] c) {
   for (int i = 0; i < a.length; i++) {
        c[i] = (a[i] * a[i] + b[i] * b[i]) * -1.0f;
   }
}
//使用向量 API 写法:
void vectorComputation(float[] a, float[] b, float[] c) {
    for (int i = 0; i < a.length; i += SPECIES.length()) {
        // VectorMask<Float>  m;
        var m = SPECIES.indexInRange(i, a.length);
        // FloatVector va, vb, vc;
        var va = FloatVector.fromArray(SPECIES, a, i, m);
        var vb = FloatVector.fromArray(SPECIES, b, i, m);
        var vc = va.mul(va)
                   .add(vb.mul(vb))
                   .neg();
        vc.intoArray(c, i, m);
    }
}
  • 引入互联网地址解析 SPI。

  • 外部函数和内存 API,引入目的是提升易用性、性能、通用性和安全性,详见 JEP 419。

  • 模式匹配 Switch 表达式。

  • 弃用 Finalization。

Java 19

Java 19 引入的主要是预览和孵化的新特性,包括:Record模式、将 JDK 移植到 Linux/RISC-V、外部函数和内存API、虚拟线程、向量API、模式匹配的 Switch、使用结构化并发方式实现并发编程。

下面主要看看一下向量 API。实例代码如下:

//不使用向量 API 的写法:
void scalarComputation(float[] a, float[] b, float[] c) {
   for (int i = 0; i < a.length; i++) {
        c[i] = (a[i] * a[i] + b[i] * b[i]) * -1.0f;
   }
}
//使用向量 API 写法:
static final VectorSpecies<Float> SPECIES = FloatVector.SPECIES_PREFERRED;

void vectorComputation(float[] a, float[] b, float[] c) {
    for (int i = 0; i < a.length; i += SPECIES.length()) {
        // VectorMask<Float>  m;
        var m = SPECIES.indexInRange(i, a.length);
        // FloatVector va, vb, vc;
        var va = FloatVector.fromArray(SPECIES, a, i, m);
        var vb = FloatVector.fromArray(SPECIES, b, i, m);
        var vc = va.mul(va)
                   .add(vb.mul(vb))
                   .neg();
        vc.intoArray(c, i, m);
    }
}

Java 20

Java 20 引入的都是孵化和预览功能,下面简单看一下:

  • 作用域值。

  • Record 模式。

  • 模式匹配的 Switch 表达式。

  • 外部函数与内存 API。

  • 虚拟线程。

  • 结构化并发。

  • 向量 API。

Java 21

Java 21 是一个 LTS 版本,新增特性比较多,其中预览和孵化特性包括:

  • 结构化并发。

  • 向量 API。

  • 作用域值。

  • 未命名类和 main 方法。

  • 未命名模式和变量。

  • 外部函数和内存 API。

  • switch 模式匹配。

正式特性包括:

  • 密钥封装机制 API,通过公钥加密来保护对称密钥。

  • 准备禁用动态加载代理。在这个版本中,如果使用动态加载代理,会出现下面警告:

WARNING: A {Java,JVM TI} agent has been loaded dynamically (file:/u/bob/agent.jar)
WARNING: If a serviceability tool is in use, please run with -XX:+EnableDynamicAgentLoading to hide this warning
WARNING: If a serviceability tool is not in use, please run with -Djdk.instrument.traceUsage for more information
WARNING: Dynamic loading of agents will be disallowed by default in a future release
  • 弃用 Windows 32 位 x86 端口;

  • 虚拟线程,虚拟线程是轻量级线程,可以减少编写、维护和观察高并发应用的工作量。下面是一个官方示例代码,首先创建一个 ExecutorService,它将为每个提交的任务创建一个虚拟线程。它将创建 10000 个虚拟线程并且等待它们执行完成。

try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
    IntStream.range(0, 10_000).forEach(i -> {
        executor.submit(() -> {
            Thread.sleep(Duration.ofSeconds(1));
            return i;
        });
    });
}  // executor.close() is called implicitly, and waits
  • switch 模式匹配。看下面模式匹配的代码:

//Java 21 以前
static String formatter(Object obj) {
    String formatted = "unknown";
    if (obj instanceof Integer i) {
        formatted = String.format("int %d", i);
    } else if (obj instanceof Long l) {
        formatted = String.format("long %d", l);
    } else if (obj instanceof Double d) {
        formatted = String.format("double %f", d);
    } else if (obj instanceof String s) {
        formatted = String.format("String %s", s);
    }
    return formatted;
}
//Java 21 以后
static String formatterPatternSwitch(Object obj) {
    return switch (obj) {
        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        -> obj.toString();
    };
}

再看一下对 null 判断的简化:

// Prior to Java 21
static void testFooBarOld(String s) {
    if (s == null) {
        System.out.println("Oops!");
        return;
    }
    switch (s) {
        case "Foo", "Bar" -> System.out.println("Great");
        default           -> System.out.println("Ok");
    }
}
// As of Java 21
static void testFooBarNew(String s) {
    switch (s) {
        case null         -> System.out.println("Oops");
        case "Foo", "Bar" -> System.out.println("Great");
        default           -> System.out.println("Ok");
    }
}

除此之外,switch 模式匹配还有很多内容,具体可以看下面的链接:

https://openjdk.org/jeps/441
  • Record Patterns,它允许我们在模式匹配中使用 record types。record types 是一种新的类声明形式,在 Java 16 中引入,用于定义不可变的数据对象。Record Patterns 提供了简单的方式来进行模式匹配,并且可以方便地从 record types 中提取参数值。看下面官方示例代码:

// As of Java 16
record Point(int x, int y) {}

static void printSum(Object obj) {
    if (obj instanceof Point p) {
        int x = p.x();
        int y = p.y();
        System.out.println(x+y);
    }
}

// As of Java 21
static void printSum(Object obj) {
    if (obj instanceof Point(int x, int y)) {
        System.out.println(x+y);
    }
}

更多精彩的示例见下面链接:

https://openjdk.org/jeps/440
  • 引入 Generational ZGC,旨在减少处理大型堆内存时可能会导致长时间的停顿。

Java 22

Java 22 引入孵化和预览的特性如下:

  • 作用域值。

  • super(...)前导语句,允许在调用 super 方法之前引入其他语句,比如参数校验。见下面代码:

//之前的写法
public class PositiveBigInteger extends BigInteger {

    public PositiveBigInteger(long value) {
        super(value);               // Potentially unnecessary work
        if (value <= 0)
            throw new IllegalArgumentException("non-positive value");
    }
}
//使用前导语句的写法
public class PositiveBigInteger extends BigInteger {
    public PositiveBigInteger(long value) {
        if (value <= 0)
            throw new IllegalArgumentException("non-positive value");
        super(value);
    }
}
  • 类文件 API。

  • String 模版。

  • 向量 API。

  • 流聚合器,引入 gather 操作,用户可以自定义中间操作,比如下面的 a、b、c

source.gather(a).gather(b).gather(c).collect(...)
  • 结构化并发。

  • 隐式声明类和实例主方法。static 修饰符、String[] 参数只有需要的时候再加。

class HelloWorld {
    void main() {
        System.out.println("Hello, World!");
    }
}

Java 22 引入的正式特性包括:

  • 启动多文件源码程序,比如一个目录下有下面的文件:

Prog1.java
Prog2.java
Helper.java
library1.jar
library2.jar

下面命令('*')可以把在目录下面的 jar 包都放到 classpath,以方便地运行 Java 程序。

java --class-path '*' Prog1.java
  • 未命名变量和模式,让代码更简洁。看下面代码示例:

//使用未命名变量之前
Queue<Integer> q = ... // x1, y1, z1, x2, y2, z2 ..
while (q.size() >= 3) {
   int x = q.remove();
   int y = q.remove();
   int z = q.remove();            // z is unused
    ... new Point(x, y) ...
}
//使用未命名变量之后
while (q.size() >= 3) {
    var x = q.remove();
    var _ = q.remove();       // Unnamed variable
    var _ = q.remove();       // Unnamed variable
    ... new Point(x, 0) ...
}
  • 外部函数和内存 API,可以方便地调用外部函数和管理内存。

Java 23

Java 23 引入孵化和预览的特性如下:

  • Patterns、instanceof 和 switch 可以使用所有基础类型。

  • 类文件 API。

  • 向量 API。

  • 流聚合器。

  • Module Import 声明:

import module java.sql
  • 隐式声明类和实例主方法。

  • 结构化并发。

  • 作用域值。

  • 灵活构造函数体。下面是示例代码:

//下面是之前的写法
public class PositiveBigInteger extends BigInteger {
    public PositiveBigInteger(long value) {
        super(value);                 // Potentially unnecessary work
        if (value <= 0) throw new IllegalArgumentException(..);
    }
}
//使用灵活构造函数体后,写法变成
public class PositiveBigInteger extends BigInteger {
    public PositiveBigInteger(long value) {
        if (value <= 0) throw new IllegalArgumentException(..);
        super(value);
    }
}

Java 23 引入的正式特性包括:

  • 弃用 sun.misc.Unsafe 中的内存访问方法。

  • ZGC 默认使用分代模式。

  • 文档注释可以使用 Markdown,看下面的官方示例,很香:

/// Returns a hash code value for the object. This method is
/// supported for the benefit of hash tables such as those provided by
/// [java.util.HashMap].
///
/// The general contract of `hashCode` is:
///
///   - Whenever it is invoked on the same object more than once during
///     an execution of a Java application, the `hashCode` method
///     must consistently return the same integer, provided no information
///     used in `equals` comparisons on the object is modified.
///     This integer need not remain consistent from one execution of an
///     application to another execution of the same application.
///   - If two objects are equal according to the
///     [equals][#equals(Object)] method, then calling the
///     `hashCode` method on each of the two objects must produce the
///     same integer result.
///   - It is _not_ required that if two objects are unequal
///     according to the [equals][#equals(Object)] method, then
///     calling the `hashCode` method on each of the two objects
///     must produce distinct integer results.  However, the programmer
///     should be aware that producing distinct integer results for
///     unequal objects may improve the performance of hash tables.
///
/// @implSpec
/// As far as is reasonably practical, the `hashCode` method defined
/// by class `Object` returns distinct integers for distinct objects.
///
/// @return  a hash code value for this object.
/// @see     java.lang.Object#equals(java.lang.Object)
/// @see     java.lang.System#identityHashCode

写在最后

其实每个 Java 版本发布的新特性并不多,而且好多特性要进行多个版本的孵化和预览。虽然公司的 Java 版本肯定跟不上 Java 版本发布的节奏,但作为程序员的我们,可以关注下 Java 的新增特性。


原文地址:https://blog.csdn.net/zjj2006/article/details/142731990

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!