您可以捐助,支持我们的公益事业。

1元 10元 50元





认证码:  验证码,看不清楚?请点击刷新验证码 必填



  求知 文章 文库 Lib 视频 iPerson 课程 认证 咨询 工具 讲座 Model Center   Code  
会员   
   
 
     
   
 
 订阅
Java21特性解读

 
作者: 江丹阳

   次浏览      
 2025-1-8
 
编辑推荐:
本文主要介绍了Java21特性相关内容 。希望对你的学习有帮助。
本文来自于微信公众号大淘宝技术,由火龙果软件Linda编辑、推荐。

当前JDK的版本已经到了23了,不过最近的LTS版本是21,刚好最近准备把直播侧serverless应用的JVM环境升级到java21(目前是11),在升级前对21的特性做一个简单的了解和熟悉,下面是个人熟悉过程中的笔记,大家可以按照每一节特性中的代码自己在本地run下,可以更快地做个了解。

JDK的版本其实最近几年开始,已经是6个月一个版本了,LTS版本大概差不多间隔4-6个版本(不定),每次升级,都会有比较多的迭代,但是主要还是集中在几个方面:1. 新特性的支持,其实主要还是面向编写和阅读的自然语言化,做的新特性的提供或者语法糖的封装,突出易懂易用;2. 内部核心实现的性能或者能力的提升,感知比较多的是gc,或者是内部的hotspot的能力等;3. bugfix,漏洞修复等。

LTS版本还是值得去了解,有条件的话也是比较推荐在生产环境去做使用的,因为不管是上述哪个方面带来的提升,对开发以及系统运维来说,都是属于易得的红利。

介绍

▐ JEP

JEP是Java Enhancement Proposal的简称,JEP的提出到生效也需要一个过程,分别是:

1. Draft(草稿)阶段:需要明确提议的动机、描述、目标和非目标(nongoals)、风险等;

2. Submission(提交)阶段:该阶段会给该提议生成一个唯一的标识,叫JAB number;

3. Review(审核)阶段:OpenJDK社区中和该提议比较相关的stakeholder会参与review,主要是看下这个提议对于Java的发展是否是必要、合理;

4. Sponsorship(赞助)阶段:这里主要是要有个开发团队去承接这个JEP的开发、测试和集成工作,该阶段还不需要实际投入开发;

5. Candidate Status(候选)阶段:进入到该阶段说明整个JEP完成立项,并且价值和作用得到了充分认同,然后是要等待在哪个版本去做试验接入;

6. Targeted(锁定版本)阶段:这个阶段已经为该JEP指定了一个JDK的版本,那需要把JEP对应的内容进行开发、测试;

7. Integrated(集成)阶段:将JEP对应的代码集成到具体版本的JDK源码中,刚集成到JDK源码中的特性一般都是preview状态,通常都需要至少1个版本迭代周期才能变成可被正常使用的状态,处于preview状态的JEP,只能被试验性使用,不能被应用到生产环境,因为功能或者说特性可能在下一个版本中就会发生变化;

8. Released(发布)阶段:该特性可以面向整体java开发者做使用,我们平时能用上的特性都属于这个范畴。

▐ SDKman

推荐一款比较不错的本地的管理各类语言版本的工具,SDKman,安装也很简单,可以进行不同版本的jdk的下载以及本地的环境切换;简单安装如下,一些命令可以参见 https://sdkman.io/

curl -s "https://get.sdkman.io" | bash

特性解读

▐ 1. Feature:Unnamed Classes and Instance Main Methods (Preview)

动机:这个特性java推出比较早了,当时没有觉得学习门槛高,因为其他当时比较流行的语言,门槛更高,而且更难读,但是随着一代代发展下来,新出来的语音在语法和表达上,更接近于NL,那一相比较,就显得java有点“年龄大,水土不服”。

//package main.java.org.example;

void main(String[] args) {

      System.out.println("Hello world!");

}

 

 

TIPS:下文中特性后面括号带Preview的,都是前瞻的功能,需要使用的话得加上 --enable-preview:

javac --release 21 --enable-preview Main_TL.java

// 或在IDEA的VM参数中添加
--enable-preview

 

▐ 2. Feature: String Templates (Preview)

动机:更直观,简洁,方便维护,同时增加了值的验证和转换。

不少JEP都有Non-Goals,这个看看还是蛮有趣的,希望是什么,不希望成为什么,这个特性的Non-Goals可以体会下:

非目标方向

- 不是面向原有的字符串操作(+)提供语法糖;

- 不是为了弃用或移除传统上用于复杂或程序化字符串组合的 StringBuilder 和 StringBuffer 类。

public class StringTemplate {

      static String anotherName = "Jiang";

       public static void main(String[] args) {

          String name = "Mario";

         String number = "987689";

         System.out.println(STR."name = \{name}, and number = \{number}, and anotherName = \{getAnotherName()}");

}

       public static String getAnotherName() {

         return anotherName;

      }

}

 

 

▐ 3. Feature: Unnamed Patterns and Variables (Preview) & Record Patterns

(这两个feature比较关联的,就一起概述了)

动机:提升可读性,并且让程序员可以清楚知道哪些成员变量被使用,哪些成员变量未被使用,preview的功能就是可以使用下划线来代替不想使用到的record里面的成员。


package org.example;

import java.util.List;

/**
* 例子中sealed是java15给出的新特性,用于限制类继承,可以看做是枚举的扩展,java17中完善和标准化<br>
* record是java14引入的新特性,用于创建不可变类,java16的时候成为正式标准
*/
public class UnnamedPatternsAndVariables {
    public static void main(String[] args) {
      process(List.of(new SealedClassA("A"), new SealedClassB("B", 123), new SealedClassC("C", 321, "hidden hobby")));
   }

   public static void process(List<SealedInterface> list) {
      for (SealedInterface sealedInterface : list) {
         if (sealedInterface instanceof SealedClassA(String name)) {
             System.out.println("The name of SealedClassA is " + name);
      } else if (sealedInterface instanceof SealedClassB(String name, Integer age)) {
          System.out.println("The name of SealedClassB is " + name + " and age is " + age);
      } else if (sealedInterface instanceof SealedClassC(String name, Integer age, String _)) {
         System.out.println("The name of SealedClassC is " + name + " and age is " + age + " and hobby is hidden");
       }
    }
}

   public sealed interface SealedInterface permits SealedClassA, SealedClassB, SealedClassC {

    }

   public record SealedClassA(String name) implements SealedInterface {

    }

   public record SealedClassB(String name, Integer age) implements SealedInterface {

    }

    public record SealedClassC(String name, Integer age, String hobby) implements SealedInterface {

}
}

 

▐ 4. Feature: Scoped Values (Preview)

特性目标:

易用性:提供一种编程模型,以便在一个线程内和子线程之间共享数据,从而简化对数据流的推理。

可理解性:使共享数据的生命周期能够从代码的语法结构中可见。

稳健性:确保调用者共享的数据只能被合法的被调用者检索。

性能:允许共享数据是不可变的,以便于被大量线程共享,并支持运行时优化。

可以重新绑定,面向之前通过上下文context传递的场景,以及使用ThreadLocal的场景;如果在中途使用异步线程进行额外操作处理,这里的值绑定会丢失,需要显示的在线程之间做传递。



package org.example;

import java.lang.ScopedValue;

public class ScopedValues {
   public static void main(String[] args) {
      ScopedValue.runWhere(EagleEye.TRACE_ID, "123456", () -> {
         RPCProcess rpcProcess = new RPCProcess();
          rpcProcess.process();
      });
    }
}

class EagleEye {
    public static final ScopedValue<String> TRACE_ID = ScopedValue.newInstance();
}

class RPCProcess {
    public void process() {
      System.out.println(STR."TRACE_ID: \{EagleEye.TRACE_ID.get()}, do rpc process");
       ServerService serverService = new ServerService();
       serverService.bizProcess();
    }
}

class ServerService {
    public void bizProcess() {
       System.out.println(STR."TRACE_ID: \{EagleEye.TRACE_ID.get()}, do biz process");
       AsyncProcessor asyncProcessor = new AsyncProcessor();
      asyncProcessor.asyncProc();
    }
}

class AsyncProcessor {
   public void asyncProc() {
       Thread thread = new Thread(new Runnable() {
          @Override
          public void run() {
             System.out.println(STR."TRACE_ID: \{!EagleEye.TRACE_ID.isBound() ? null : EagleEye.TRACE_ID.get()}, do async process");
       }
       });
       thread.start();
    }
}

 

 

▐ 5. Feature: Foreign Function & Memory API (Third Preview)

动机:为了Java程序员提供更可靠的操作native代码和存储(操作系统层面)的API能力。

JDK19的时候首次提出,JDK20第二版preivew,JDK21第三版preview;该特性主要是为了提供一种更高效、便捷和安全的方式,去调用系统底层的类库,但是难点个人感觉还是在调用安全性上,这类工具在生产环境中的应用都需要比较慎重,因为都是属于运行时的错误,而且不容易测试发现。


package org.example;

import java.lang.foreign.Arena;
import java.lang.foreign.MemorySegment;
import java.lang.foreign.ValueLayout;

public class ForeignFunctionMemory {
    public static void main(String[] args) {
       MemorySegment memorySegment = Arena.ofAuto().allocate(1024);

       memorySegment.set(ValueLayout.JAVA_BYTE, 0, (byte) 1);
       byte target = memorySegment.get(ValueLayout.JAVA_BYTE, 0);

      System.out.println(target);
    }
}

▐ 6. Feature: Structured Concurrency (Preview)

动机:之前不同子的并发任务之间基本是独立,需要程序员自己去管理、组装以及显式调度,难免会出现一些线程泄漏(未正确关闭或回收线程)和取消延迟等问题;该特性是通过引入Scope的概念,简化java并发编程,把并发编程当成一个整体,把线程的管理和错误处理做了统一封装,让并发编程更傻瓜化,该特性在后续配合虚拟线程应该会有更广的应用。


package org.example;

import java.util.concurrent.StructuredTaskScope;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;

   public class StructuredConcurrency {
public static void main(String[] args) {

      try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
          Supplier<String> task1 = scope.fork(() -> {
            System.out.println("task1 is running...");
            TimeUnit.SECONDS.sleep(3);
            System.out.println("task1 is done.");
         nbsp;   return "Hello, Mario";
         });

      Supplier<Integer> task2 = scope.fork(() -&   gt; {
            System.out.println("task2 is running...");
            TimeUnit.SECONDS.sleep(1);
            System.out.println("task2 is done.");
            return 666;
         });

         scope.join().throwIfFailed();
         System.out.println("all tasks are done.");

         System.out.println("Task1 result:" + task1.get() + "\nTask2 result: " + task2.get());
      } catch (Exception e) {
      nbsp;   throw new RuntimeException(e);
       }
    }
}

new StructuredTaskScope.ShutdownOnFailure()默认使用的是虚拟线程,如果是要用普通的系统线程的话,可以调用另外一个构造方法,传入ThreadFactory即可。

▐ 7. Feature: Vector API (Sixth Incubator)

引入一个API,用于表示向量计算,该API能够在运行时可靠地编译为支持的CPU架构上的最佳向量指令,从而实现比等效的标量计算更优越的性能。第六版了,从java16推出,然后17、18、19、20,到现在21都有做进一步的迭代孵化,现在依旧是个孵化阶段。

关注三个点:性能、可移植性(面向不同的CPU架构)、可靠性(即使在一些特殊的CPU架构下不能完美的利用硬件特性,但是也要保证在一定的效率下去正确的执行)。

package org.example;

import jdk.incubator.vector.IntVector;
import jdk.incubator.vector.VectorSpecies;

public class VectorAPIDemo {

   public static void main(String[] args) {
      int[] array1 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
       int[] array2 = {11, 12, 13, 14, 15, 16, 17, 18, 19, 20};
       int[] result = new int[array1.length];

       VectorSpecies<Integer> vectorSpecies = IntVector.SPECIES_64;
       System.out.println("Vector Species Length: " + vectorSpecies.length());

       for (int i = 0; i < array1.length; i += vectorSpecies.length()) {
          System.out.println("Vector Loop, i: " + i);
          IntVector vector1 = IntVector.fromArray(vectorSpecies, array1, i);
          IntVector vector2 = IntVector.fromArray(vectorSpecies, array2, i);

         IntVector resultVector = vector1.add(vector2);
         resultVector.intoArray(result, i);
          System.out.printf("Result Vector: %s, and Result Array: %s%n", resultVector, toString(result));
    }

      for (int j : result) {
          System.out.println(j);
       }
}

public static String toString(int[] result) {
      StringBuilder sb = new StringBuilder();
       for (int j : result) {
         sb.append(j).append(",");
       }
       return sb.toString();
    }
}

 

 


javac --add-modules jdk.incubator.vector VectorAPI.java
java --add-modules jdk.incubator.vector VectorAPI.java

 

问题1:这里的向量内部数据类型都是封装类型,主要受限于java的泛型机制,目前有个叫Valhalla的项目在推进增强java泛型的能力;

问题2:对于x64中的SIMD(是一种单指令多数据的并行处理技术)支持不太好,从运行性能上无法充分利用硬件特性;

问题3:发现对于mac的M系列芯片也支持不太好,如果不是自身指定正确的Species的大小,都会出现bound越界的问题。

▐ 8. Feature: Pattern Matching for switch

动机:增强Switch语法中对于数据类型的识别能力,提升Switch基于数据类型判断上的逻辑处理能力。


public static void main(String[] args) {
    System.out.println("Hello world!");

   System.out.println(formatterPatternSwitch(123)); // 输出: int 123
    System.out.println(formatterPatternSwitch(456L)); // 输出: long 456
   System.out.println(formatterPatternSwitch(789.0)); // 输出: double 789.000000
   System.out.println(formatterPatternSwitch("Hello")); // 输出: String Hello
   System.out.println(formatterPatternSwitch(new Object())); // 输出: java.lang.Object@<hashcode>
}

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

 

 

▐ 9. Feature: Sequenced Collections

动机:对于有序的集合或者map类型的操作支持不够好,同时差异化比较大,所以新增加几个超类,来定义针对这类有序集合的操作,做了一个统一。


interface SequencedCollection<E> extends Collection<E> {
    // new method
    SequencedCollection<E> reversed();
    // methods promoted from Deque
    void addFirst(E);
   void addLast(E);
   E getFirst();
    E getLast();
    E removeFirst();
   E removeLast();
    }

 


interface SequencedMap<K,V> extends Map<K,V> {
    // new methods
    SequencedMap<K,V> reversed();
    SequencedSet<K> sequencedKeySet();
    SequencedCollection<V> sequencedValues();
    SequencedSet<Entry<K,V>> sequencedEntrySet();
    V putFirst(K, V);
   V putLast(K, V);
   // methods promoted from NavigableMap
   Entry<K, V> firstEntry();
   Entry<K, V> lastEntry();
   Entry<K, V> pollFirstEntry();
   Entry<K, V> pollLastEntry();
}

 


public class SequencedCollections {
    public static void main(String[] args) {
      LinkedHashSet<Integer> hashSet = new LinkedHashSet<>();

      hashSet.add(10);
      hashSet.add(20);
      hashSet.add(30);
       hashSet.add(40);
       hashSet.add(50);
      hashSet.add(60);
      hashSet.add(70);
      hashSet.addFirst(0);
      hashSet.addLast(80);

      System.out.println(STR."hashSet = \{hashSet} , first = \{hashSet.getFirst()} , last = \{hashSet.getLast()}, reversed= \{hashSet.reversed()}");
}
}

 

▐ 10. Feature: Generational ZGC

动机:提升ZGC在面向不同JVM堆大小的性能和效率问题(之前是面向大堆有更好的性能收益,小堆可能未有明显收益或负向收益)。

ZGC是在java11点时候推出的,从java15开始基本上可以在生产环境下使用;ZGC的STW时间是微秒级,G1是毫秒到秒级别;ZGC 目前将所有对象存储在一起,而不考虑对象的年龄,因此每次运行时都必须收集所有对象,但是年轻对象相比年老对象(old objects)生命周期更短,因此其实对于年轻对象增加GC的频次可以在效率以及内存回收收益上都是比较可观的。

G1和ZGC都不属于传统意义上的分代垃圾回收器,但是G1里面还是通过监测和跟踪对象的存活周期做不同策略的回收,不过就目前看来这样的方式,都不如分代设计来的高效。

之前是non-generational的模式,就是面向一整块堆栈做操作,简单示意下:

这次的优化是增加一种叫generational的模式,非常类似之前的分代设计;

在21中要使用ZGC的话,在JVM启动参数里面要加入如下配置:


-XX:+UseZGC -XX:+ZGenerational

 

▐ 11. Feature: Virtual Threads

虚拟线程是轻量的,对资源诉求非常小的实现,不需要为虚拟线程去构建线程池,比较好的做法是快速使用,快速释放,短平快,虚拟线程替代不了原有的平台线程,但是在一些非CPU密集也不是IO密集的操作上会更合适,比如像处理网络类的请求,或者是一些非常简单的并发的任务。


package org.example;

import java.util.Scanner;

public class VirtualThreads implements Runnable{
    public static void main(String[] args) {
      Scanner scanner = new Scanner(System.in);
      System.out.println("Use virtual threads? (true/false)");
      boolean useVirtual = scanner.nextBoolean();
       System.out.println("Use virtual threads: " + useVirtual);

      long start = System.currentTimeMillis();

      for (int i=0; i<100000; i++) {
      if (useVirtual) {
            Thread.startVirtualThread(new VirtualThreads());
      } else {
      Thread thread = new Thread(new VirtualThreads());
            thread.start();
}
}

      long end = System.currentTimeMillis();
      System.out.println("Time: " + (end - start));
}

@Override
public void run() {
      // empty
}
}

 

▐ 12. Feature: Key Encapsulation Mechanism API

针对KEM提供相关的API和类库,KEM是一种用于安全密钥交换的技术,目标是保障交互的双方的密钥约定生成和传输的安全性。


package org.example;

import javax.crypto.KEM;
import java.security.*;
import java.util.Arrays;
import java.util.Base64;

  public class KEMDemo {
       public static void main(String[] args) throws Exception {
      KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("X25519");
      KeyPair keyPair = keyPairGenerator.generateKeyPair();

      SecureRandom random = new SecureRandom();
      KEM kemS = KEM.getInstance("DHKEM");
      KEM.Encapsulated encapsulated = kemS.newEncapsulator(keyPair.getPublic(), random).encapsulate();
      byte[] encapsulatedKey = encapsulated.key().getEncoded();
      byte[] encapsulatedSymmetricKey = encapsulated.encapsulation();
      byte[] decryptedSymmetricKey = kemS.newDecapsulator(keyPair.getPrivate()).decapsulate(encapsulatedSymmetricKey).getEncoded();

       System.out.println("encapsulatedKey: " + Base64.getEncoder().encodeToString(encapsulatedKey));
      System.out.println("encapsulatedSymmetricKey: " + Base64.getEncoder().encodeToString(encapsulatedSymmetricKey));
      System.out.println("decryptedSymmetricKey: " + Base64.getEncoder().encodeToString(decryptedSymmetricKey));

       System.out.println("encapsulatedKey and decryptedSymmetricKey is equal ? " + Arrays.equals(encapsulatedKey, decryptedSymmetricKey));
}
}

 

结语

整体从Feature内容看,个人体感STR在使用上,还是提升不少便捷性的,在试用的时候也非常乐意去使用,期待后续尽早变成release状态;21中给到比较大的提升,个人认为是虚拟线程和ZGC,这两块后面升级后会做个应用实践,欢迎已经有实践经验的或者是有意向一起探索的小伙伴来交流。



 

 
   
次浏览       
相关文章

Java微服务新生代之Nacos
深入理解Java中的容器
Java容器详解
Java代码质量检查工具及使用案例
相关文档

Java性能优化
Spring框架
SSM框架简单简绍
从零开始学java编程经典
相关课程

高性能Java编程与系统性能优化
JavaEE架构、 设计模式及性能调优
Java编程基础到应用开发
JAVA虚拟机原理剖析

最新活动计划
SysML和EA系统设计与建模 1-16[北京]
企业架构师(业务、应用、技术) 1-23[北京]
大语言模型(LLM)Fine Tune 2-22[在线]
MBSE(基于模型的系统工程)2-27[北京]
OpenGauss数据库调优实践 3-11[北京]
UAF架构体系与实践 3-25[北京]
 
 
最新文章
Java虚拟机架构
JVM——Java虚拟机架构
Java容器详解
Java进阶--深入理解ArrayList实现原理
Java并发容器,底层原理深入分析
最新课程
java编程基础到应用开发
JavaEE架构、 设计模式及性能调优
高性能Java编程与系统性能优化
SpringBoot&Cloud、JavaSSM框架
Spring Boot 培训
更多...   
成功案例
国内知名银行 Spring+SpringBoot+Cloud+MVC
北京 Java编程基础与网页开发基础
北京 Struts+Spring
华夏基金 ActiveMQ 原理
某民航公 Java基础编程到应用开发
更多...