Java語言特性系列
- Java5的新特性
- Java6的新特性
- Java7的新特性
- Java8的新特性
- Java9的新特性
- Java10的新特性
- Java11的新特性
- Java12的新特性
- Java13的新特性
- Java14的新特性
- Java15的新特性
- Java16的新特性
- Java17的新特性
- Java18的新特性
- Java19的新特性
- Java20的新特性
- Java21的新特性
序
本文主要講述一下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,這裏舉幾個例子。
添加項
- Support Unicode 15.0 Update Unicode Data Files to 15.0.0
- Add GarbageCollectorMXBean for Remark and Cleanup Pause Time in G1 JDK-8297247
移除項
- 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
- java.net.URL Constructors Are Deprecated JDK-8294241
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
其他事項
- Disabled TLS_ECDH_* Cipher Suites (JDK-8279164)
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主要有如下幾個特性
- JEP 429: Scoped Values (Incubator)
- JEP 432: Record Patterns (Second Preview)
- JEP 433: Pattern Matching for switch (Fourth Preview)
- JEP 434: Foreign Function & Memory API (Second Preview)
- JEP 436: Virtual Threads (Second Preview)
- JEP 437: Structured Concurrency (Second Incubator)
- JEP 438: Vector API (Fifth Incubator)
doc
- JDK 20 Features
- JDK 20 Release Notes
- Consolidated JDK 20 Release Notes
- Java SE 20 deprecated-list
- The Arrival of Java 20
- JDK 20 G1/Parallel/Serial GC changes
- Java 20 Delivers Features for Projects Amber, Loom and Panama
- Java 20: a faster future is Looming
- JDK 20 Security Enhancements
- Project Loom’s Scoped Values: The Most Interesting New Java 20 Feature