Java20的新特性

Java语言特性系列

本文主要讲述一下Java20的新特性

版本号

java -version
openjdk version "20" 2023-03-21
OpenJDK Runtime Environment (build 20+36-2344)
OpenJDK 64-Bit Server VM (build 20+36-2344, mixed mode, sharing)

从version信息可以看出是build 20+36

特性列表

JEP 429: Scoped Values (Incubator)

ScopedValue是一种类似ThreadLocal的线程内/父子线程传递变量的更优方案。ThreadLocal提供了一种无需在方法参数上传递通用变量的方法,InheritableThreadLocal使得子线程可以拷贝继承父线程的变量。但是ThreadLocal提供了set方法,变量是可变的,另外remove方法很容易被忽略,导致在线程池场景下很容易造成内存泄露。ScopedValue则提供了一种不可变、不拷贝的方案,即不提供set方法,子线程不需要拷贝就可以访问父线程的变量。具体使用如下:

class Server {
  public final static ScopedValue<User> LOGGED_IN_USER = ScopedValue.newInstance();
 
  private void serve(Request request) {
    // ...
    User loggedInUser = authenticateUser(request);
    ScopedValue.where(LOGGED_IN_USER, loggedInUser)
               .run(() -> restAdapter.processRequest(request));
    // ...
  }
}

通过ScopedValue.where可以绑定ScopedValue的值,然后在run方法里可以使用,方法执行完毕自行释放,可以被垃圾收集器回收

JEP 432: Record Patterns (Second Preview)

JDK19的JEP 405: Record Patterns (Preview)将Record的模式匹配作为第一次preview
JDK20则作为第二次preview

  • 针对嵌套record的推断,可以这样
record Point(int x, int y) {}
enum Color { RED, GREEN, BLUE }
record ColoredPoint(Point p, Color c) {}
record Rectangle(ColoredPoint upperLeft, ColoredPoint lowerRight) {}

static void printColorOfUpperLeftPoint(Rectangle r) {
    if (r instanceof Rectangle(ColoredPoint(Point p, Color c),
                               ColoredPoint lr)) {
        System.out.println(c);
    }
}
  • 整体而言,模式匹配有如下几种:
Pattern:
  TypePattern
  ParenthesizedPattern
  RecordPattern

TypePattern:
  LocalVariableDeclaration

ParenthesizedPattern:
  ( Pattern )

RecordPattern:
  ReferenceType RecordStructurePattern

RecordStructurePattern:
  ( [ RecordComponentPatternList ] )

RecordComponentPatternList : 
  Pattern { , Pattern }
  • 针对泛型推断
record Box<T>(T t) {}

static void test1(Box<String> bo) {
    if (bo instanceof Box<String>(var s)) {
        System.out.println("String " + s);
    }
}

也支持嵌套

static void test3(Box<Box<String>> bo) {
    if (bo instanceof Box<Box<String>>(Box(var s))) {    
        System.out.println("String " + s);
    }
}

JEP 433: Pattern Matching for switch (Fourth Preview)

在JDK14JEP 305: Pattern Matching for instanceof (Preview)作为preview
在JDK15JEP 375: Pattern Matching for instanceof (Second Preview)作为第二轮的preview
在JDK16JEP 394: Pattern Matching for instanceof转正
JDK17引入JEP 406: Pattern Matching for switch (Preview)
JDK18的JEP 420: Pattern Matching for switch (Second Preview)则作为第二轮preview
JDK19的JEP 427: Pattern Matching for switch (Third Preview)作为第三轮preview
JDK20作为第四轮preview
自第三次预览以来的主要变化是:

  • 针对枚举类型出现无法匹配的时候抛出MatchException而不是IncompatibleClassChangeError
  • 开关标签的语法更简单
  • switch现在支持record泛型的推断

以前针对null值switch会抛出异常,需要特殊处理

static void testFooBar(String s) {
    if (s == null) {
        System.out.println("Oops!");
        return;
    }
    switch (s) {
        case "Foo", "Bar" -> System.out.println("Great");
        default           -> System.out.println("Ok");
    }
}

现在可以直接switch

static void testFooBar(String s) {
    switch (s) {
        case null         -> System.out.println("Oops");
        case "Foo", "Bar" -> System.out.println("Great");
        default           -> System.out.println("Ok");
    }
}

case when的支持,以前这么写

class Shape {}
class Rectangle extends Shape {}
class Triangle  extends Shape { int calculateArea() { ... } }

static void testTriangle(Shape s) {
    switch (s) {
        case null:
            break;
        case Triangle t:
            if (t.calculateArea() > 100) {
                System.out.println("Large triangle");
                break;
            }
        default:
            System.out.println("A shape, possibly a small triangle");
    }
}

现在可以这么写

static void testTriangle(Shape s) {
    switch (s) {
        case null -> 
            { break; }
        case Triangle t
        when t.calculateArea() > 100 ->
            System.out.println("Large triangle");
        case Triangle t ->
            System.out.println("Small triangle");
        default ->
            System.out.println("Non-triangle");
    }
}

针对record泛型的类型推断:

record MyPair<S,T>(S fst, T snd){};

static void recordInference(MyPair<String, Integer> pair){
    switch (pair) {
        case MyPair(var f, var s) -> 
            ... // Inferred record Pattern MyPair<String,Integer>(var f, var s)
        ...
    }
}

JEP 434: Foreign Function & Memory API (Second Preview)

Foreign Function & Memory (FFM) API包含了两个incubating API
JDK14的JEP 370: Foreign-Memory Access API (Incubator)引入了Foreign-Memory Access API作为incubator
JDK15的JEP 383: Foreign-Memory Access API (Second Incubator)Foreign-Memory Access API作为第二轮incubator
JDK16的JEP 393: Foreign-Memory Access API (Third Incubator)作为第三轮,它引入了Foreign Linker API (JEP 389)
FFM API在JDK 17的JEP 412: Foreign Function & Memory API (Incubator)作为incubator引入
FFM API在JDK 18的JEP 419: Foreign Function & Memory API (Second Incubator)作为第二轮incubator
JDK19的JEP 424: Foreign Function & Memory API (Preview)则将FFM API作为preview API
JDK20作为第二轮preview

使用示例

 javac --release 20 --enable-preview ... and java --enable-preview ....

// 1. Find foreign function on the C library path
Linker linker          = Linker.nativeLinker();
SymbolLookup stdlib    = linker.defaultLookup();
MethodHandle radixsort = linker.downcallHandle(stdlib.find("radixsort"), ...);
// 2. Allocate on-heap memory to store four strings
String[] javaStrings = { "mouse", "cat", "dog", "car" };
// 3. Use try-with-resources to manage the lifetime of off-heap memory
try (Arena offHeap = Arena.openConfined()) {
    // 4. Allocate a region of off-heap memory to store four pointers
    MemorySegment pointers = offHeap.allocateArray(ValueLayout.ADDRESS, javaStrings.length);
    // 5. Copy the strings from on-heap to off-heap
    for (int i = 0; i < javaStrings.length; i++) {
        MemorySegment cString = offHeap.allocateUtf8String(javaStrings[i]);
        pointers.setAtIndex(ValueLayout.ADDRESS, i, cString);
    }
    // 6. Sort the off-heap data by calling the foreign function
    radixsort.invoke(pointers, javaStrings.length, MemorySegment.NULL, '\0');
    // 7. Copy the (reordered) strings from off-heap to on-heap
    for (int i = 0; i < javaStrings.length; i++) {
        MemorySegment cString = pointers.getAtIndex(ValueLayout.ADDRESS, i);
        javaStrings[i] = cString.getUtf8String(0);
    }
} // 8. All off-heap memory is deallocated here
assert Arrays.equals(javaStrings, new String[] {"car", "cat", "dog", "mouse"});  // true

JEP 436: Virtual Threads (Second Preview)

在JDK19[https://openjdk.org/jeps/425](JEP 425: Virtual Threads (Preview))作为第一次preview
在JDK20作为第二次preview,此版本java.lang.ThreadGroup被永久废弃

使用示例

void handle(Request request, Response response) {
    var url1 = ...
    var url2 = ...
 
    try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
        var future1 = executor.submit(() -> fetchURL(url1));
        var future2 = executor.submit(() -> fetchURL(url2));
        response.send(future1.get() + future2.get());
    } catch (ExecutionException | InterruptedException e) {
        response.fail(e);
    }
}
 
String fetchURL(URL url) throws IOException {
    try (var in = url.openStream()) {
        return new String(in.readAllBytes(), StandardCharsets.UTF_8);
    }
}

JEP 437: Structured Concurrency (Second Incubator)

在JDK19JEP 428: Structured Concurrency (Incubator)作为第一次incubator
在JDK20作为第二次incubator

JEP 438: Vector API (Fifth Incubator)

JDK16引入了JEP 338: Vector API (Incubator)提供了jdk.incubator.vector来用于矢量计算
JDK17进行改进并作为第二轮的incubatorJEP 414: Vector API (Second Incubator)
JDK18的JEP 417: Vector API (Third Incubator)进行改进并作为第三轮的incubator
JDK19JEP 426:Vector API (Fourth Incubator)作为第四轮的incubator
JDK20作为第五轮的incubator

细项解读

上面列出的是大方面的特性,除此之外还有一些api的更新及废弃,主要见JDK 20 Release Notes,这里举几个例子。

添加项

移除项

  • Thread.suspend/resume Changed to Throw UnsupportedOperationException JDK-8249627
  • Thread.Stop Changed to Throw UnsupportedOperationException JDK-8289610
  • Improved Control of G1 Concurrent Refinement Threads JDK-8137022

以下这些参数未来版本移除

-XX:-G1UseAdaptiveConcRefinement

-XX:G1ConcRefinementGreenZone=buffer-count

-XX:G1ConcRefinementYellowZone=buffer-count

-XX:G1ConcRefinementRedZone=buffer-count

-XX:G1ConcRefinementThresholdStep=buffer-count

-XX:G1ConcRefinementServiceIntervalMillis=msec

废弃项

完整列表见Java SE 20 deprecated-list

URL的构造器被废弃,可以使用URL::of(URI, URLStreamHandler)工厂方法替代

已知问题

  • java.lang.Float.floatToFloat16 and java.lang.Float.float16ToFloat May Return Different NaN Results when Optimized by the JIT Compiler (JDK-8302976, JDK-8289551, JDK-8289552)

JDK20引入了java.lang.Float.floatToFloat16及java.lang.Float.float16ToFloat方法,在JIT编译优化时可能会返回不同的Nan结果,可以使用如下参数禁用JIT对此的优化

-XX:+UnlockDiagnosticVMOptions -XX:DisableIntrinsic=_floatToFloat16,_float16ToFloat

其他事项

TLS_ECDH_* cipher suites默认被禁用了

  • HTTP Response Input Streams Will Throw an IOException on Interrupt (JDK-8294047)
  • HttpClient Default Keep Alive Time is 30 Seconds (JDK-8297030)

http1.1及http2的空闲连接的超时时间从1200秒改为30秒

小结

Java20主要有如下几个特性

doc

### 三、Java 20新特性与改进 #### 1. 模块化 Java 20 继续推进模块化功能的完善,通过模块系统(JPMS - Java Platform Module System),开发者可以更清晰地定义代码的依赖关系,提高代码的可维护性和安全性。模块化使得大型应用程序能够更好地组织和管理其组件,从而提升开发效率和运行性能[^1]。 #### 2. 结构化任务作用域(Structured Task Scope) Java 20 引入了`StructuredTaskScope`,这是一个新的并发工具,用于简化多线程任务的管理和协调。通过使用`StructuredTaskScope`,开发者可以更直观地派生、管理和组合多个并发任务。例如,可以使用`fork()`方法启动多个任务,并通过`join()`方法等待它们完成。此外,`throwIfFailed()`方法能够检测任务是否执行失败,并在必要时抛出异常。以下是一个示例代码: ```java Response handle() throws ExecutionException, InterruptedException { try (var scope = new StructuredTaskScope.ShutdownOnFailure()) { Future<String> user = scope.fork(() -> findUser()); Future<Integer> order = scope.fork(() -> fetchOrder()); scope.join(); // 等待所有任务完成 scope.throwIfFailed(); // 检查任务是否失败 // 如果所有任务成功,组合结果 return new Response(user.resultNow(), order.resultNow()); } } ``` 该特性极大地简化了并发编程的复杂性,提高了代码的可读性和可维护性[^3]。 #### 3. 向量 API(Vector API) Java 20 继续孵化向量 API,这是为了支持硬件加速计算而引入的新功能。向量 API 允许开发者编写高性能的数值计算代码,利用现代 CPU 的 SIMD(Single Instruction, Multiple Data)指令集来加速数据处理。向量 API 的第五轮孵化版本进一步优化了其性能和稳定性,为未来的正式发布奠定了基础。该特性特别适用于数据处理密集型应用,如机器学习、图像处理和科学计算[^3]。 #### 4. UTF-8 编码标准化 虽然 UTF-8 编码的支持在 Java 18 中就已经被引入,但 Java 20 进一步巩固了其在跨平台开发中的地位。通过强制使用 UTF-8 编码,Java 20 解决了不同操作系统和环境中可能出现的乱码问题,确保了文本数据在不同平台间的一致性和可靠性。这对于国际化和跨平台项目尤为重要[^2]。 #### 5. jwebserver 快速原型开发 Java 20 继续支持 jwebserver 工具,这是一个轻量级的 HTTP 服务器,适用于快速原型开发和测试。开发者可以使用 jwebserver 快速搭建一个简单的 Web 服务,而无需依赖复杂的框架或配置。这对于调试和演示非常有用,尤其是在开发初期阶段。 #### 6. 文档注释中的 @snippet 标签 Java 20 延续了 Java 18 引入的 `@snippet` 标签,用于在 Javadoc 中嵌入代码片段。`@snippet` 标签允许开发者在文档中直接插入代码示例,而不必担心 HTML 转义字符的问题。这不仅提高了文档的可读性,还使得代码示例更容易维护和更新。例如: ```java /** * 示例方法 * * @param input 输入参数 * @return 返回处理结果 * * {@snippet : * public String exampleMethod(String input) { * return "Hello, " + input; * } * } */ ``` 该特性使得 API 文档更加直观和易于理解[^2]。 #### 7. 反射性能优化 Java 20 进一步优化了反射机制的性能,使得反射调用的速度更快。这对于依赖反射的框架(如 Spring 和 Hibernate)来说是一个重要的改进,能够显著提升应用程序的整体性能。反射性能的飞跃不仅提高了开发效率,还增强了框架的灵活性和可扩展性。 ### 总结 Java 20 引入了一系列新特性和改进,涵盖了模块化、并发编程、向量计算、编码标准化、快速原型开发、文档编写和反射性能优化等多个方面。这些新特性不仅提升了 Java 的性能和易用性,还为开发者提供了更多的工具和灵活性,帮助他们构建更加高效和可靠的应用程序。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值