作者:Grey
原文地址:
映象倉庫: GitCode:java_new_features
Java SE 19,構造雜湊表的時候,由於有擴容因子 0.75 的設定,所以如果要開闢一個 120 空間的雜湊表,需要如下定義
Map<Integer,Integer> map1 = new HashMap<>(160);
Java SE 19 中,HashMap 有了新的構造方法,可以用 newHashMap 直接指定具體大小,不需要提前做換算。
這個用法類似 Guava 的集合構造方式。
如上例,可以使用
Map<Integer, Integer> map2 = HashMap.newHashMap(120);
程式碼如下
import java.util.*;
public class NewHashMapMethodTest {
public static void main(String[] arg) {
// jdk 19之前
// 由於有 擴容因子 0.75 的設定,所以如果要開闢一個120的雜湊表,需要如下定義
Map<Integer,Integer> map1 = new HashMap<>(160);
for (int i = 0; i < 10; i++) {
map1.put(i,i);
}
System.out.println(map1);
// jdk 19及以後
// 可以用newHashMap直接指定具體大小,不需要提前做換算
Map<Integer, Integer> map2 = HashMap.newHashMap(120);
for (int i = 0; i < 10; i++) {
map2.put(i,i);
}
System.out.println(map2);
}
}
首次引入這個功能是在Java SE 17
switch (obj) {
case String s && s.length() > 5 -> System.out.println(s.toUpperCase())。
...
}
我們可以在 switch 語句中檢查一個物件是否屬於某個特定的類,以及它是否有額外的特徵(比如在例子中:長於五個字元)。
在 Java SE 19 中,我們必須使用新的關鍵字 when 來代替 &&
完整程式碼如下
package git.snippets.jdk19;
/**
* switch 增強 第三次預覽
* 需要增加 --enable-preview引數
*
* @author <a href="mailto:[email protected]">Grey</a>
* @date 2022/9/22
* @since 19
*/
public class SwitchEnhancedTest {
public static void main(String[] args) {
checkObjSince19("hello world");
}
public static void checkObjSince19(Object when) {
// when 是一個所謂的 "上下文關鍵字",因此只在一個 case 標籤中具有意義。如果你的程式碼中有名稱為 "when "的變數或方法,你不需要改變它們。
switch (when) {
case String s when s.length() > 5 -> System.out.println(s.toUpperCase());
case String s -> System.out.println(s.toLowerCase());
case Integer i -> System.out.println(i * i);
default -> {
}
}
}
}
when 是一個所謂的 "上下文關鍵字",因此只在一個 case 標籤中具有意義。如果你的程式碼中有名稱為 "when "的變數或方法,你不需要改變它們。
switch 和 instanceof 的增強匹配(Java SE 16 新增特性)功能現在可以用於 record,範例程式碼如下
package git.snippets.jdk19;
/**
* record 模式匹配增強
* 需要增加 --enable-preview引數
*
* @author <a href="mailto:[email protected]">Grey</a>
* @date 2022/9/22
* @since 19
*/
public class RecordTest {
public static void main(String[] args) {
Points points = new Points(1, 2);
Line line = new Line(new Points(1, 2), new Points(3, 4));
printPoints(points);
printLine(line);
}
private static void printPoints(Object object) {
if (object instanceof Points(int x,int y)) {
System.out.println("jdk 19 object is a position, x = " + x + ", y = " + y);
}
if (object instanceof Points points) {
System.out.println("pre jdk 19 object is a position, x = " + points.x()
+ ", y = " + points.y());
}
switch (object) {
case Points position -> System.out.println("pre jdk 19 object is a position, x = " + position.x()
+ ", y = " + position.y());
default -> throw new IllegalStateException("Unexpected value: " + object);
}
switch (object) {
case Points(int x,int y) -> System.out.println(" jdk 19 object is a position, x = " + x
+ ", y = " + y);
default -> throw new IllegalStateException("Unexpected value: " + object);
}
}
public static void printLine(Object object) {
if (object instanceof Line(Points(int x1,int y1),Points(int x2,int y2))) {
System.out.println("object is a path, x1 = " + x1 + ", y1 = " + y1
+ ", x2 = " + x2 + ", y2 = " + y2);
}
switch (object) {
case Line(Points(int x1,int y1),Points(int x2,int y2)) ->
System.out.println("object is a path, x1 = " + x1 + ", y1 = " + y1
+ ", x2 = " + x2 + ", y2 = " + y2);
// other cases ...
default -> throw new IllegalStateException("Unexpected value: " + object);
}
}
}
record Points(int x, int y) {
}
record Line(Points from, Points to) {
}
虛擬執行緒在Project Loom中已經開發了好幾年,到目前為止只能用自編譯的JDK進行測試。
具體可以檢視這篇文章:Virtual Threads in Java (Project Loom)
在Project Panama中,取代繁瑣、易出錯、速度慢的 Java 本地介面(JNI)的工作已經進行了很長時間。
在 Java 14 和 Java 16 中已經引入了 "外來記憶體存取 API "和 "外來連結器 API"--最初都是單獨處於孵化階段。在 Java 17 中,這些 API 被合併為 "Foreign Function & Memory API"(FFM API),直到 Java 18,它一直處於孵化階段。
在 Java 19 中,JDK Enhancement Proposal 424最終將新的 API 提升到了預覽階段,
FFM API 可以直接從 Java 存取本地記憶體(即 Java 堆外的記憶體)和存取原生程式碼(如 C 庫)。
下面是一個簡單的例子,它在堆外記憶體中儲存一個字串,並對其呼叫 C 語言標準庫的 "strlen "函數。
package git.snippets.jdk19;
import java.lang.foreign.FunctionDescriptor;
import java.lang.foreign.Linker;
import java.lang.foreign.MemorySegment;
import java.lang.foreign.SymbolLookup;
import java.lang.invoke.MethodHandle;
import static java.lang.foreign.SegmentAllocator.implicitAllocator;
import static java.lang.foreign.ValueLayout.ADDRESS;
import static java.lang.foreign.ValueLayout.JAVA_LONG;
/**
* FFM API
* @author <a href="mailto:[email protected]">Grey</a>
* @date 2022/9/22
* @since 19
*/
public class FFMTest {
public static void main(String[] args) throws Throwable {
// 1. Get a lookup object for commonly used libraries
SymbolLookup stdlib = Linker.nativeLinker().defaultLookup();
// 2. Get a handle to the "strlen" function in the C standard library
MethodHandle strlen = Linker.nativeLinker().downcallHandle(
stdlib.lookup("strlen").orElseThrow(),
FunctionDescriptor.of(JAVA_LONG, ADDRESS));
// 3. Convert Java String to C string and store it in off-heap memory
MemorySegment str = implicitAllocator().allocateUtf8String("Happy Coding!");
// 4. Invoke the foreign function
long len = (long) strlen.invoke(str);
System.out.println("len = " + len);
}
}
如果一個任務由不同的子任務組成,可以並行完成(例如,從資料庫存取資料、呼叫遠端 API 和載入檔案),我們可以使用 Java 多執行緒的一些工具類來完成。
例如:
private final ExecutorService executor = Executors.newCachedThreadPool();
// jdk 19 之前
public Invoice createInvoice(int orderId, int customerId, String language) throws ExecutionException, InterruptedException {
Future<Order> orderFuture = executor.submit(() -> loadOrderFromOrderService(orderId));
Future<Customer> customerFuture = executor.submit(() -> loadCustomerFromDatabase(customerId));
Future<String> invoiceTemplateFuture = executor.submit(() -> loadInvoiceTemplateFromFile(language));
Order order = orderFuture.get();
Customer customer = customerFuture.get();
String invoiceTemplate = invoiceTemplateFuture.get();
return Invoice.generate(order, customer, invoiceTemplate);
}
但是:
如果一個子任務發生錯誤--我們如何取消其他子任務?
如果某個子任務不需要了,我們如何取消這個子任務呢?
這兩種情況都可以,但需要相當複雜和難以維護的程式碼。
而如果我們想對這種型別的並行程式碼進行偵錯也非常麻煩。
JDK Enhancement Proposal 428為所謂的 "結構化並行 "引入了一個 API,這個概念旨在改善這種型別需求的程式碼的實現、可讀性和可維護性。
使用 StructuredTaskScope,我們可以把這個例子改寫成如下。
public Invoice createInvoiceSinceJava19(int orderId, int customerId, String language)
throws ExecutionException, InterruptedException {
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
Future<Order> orderFuture =
scope.fork(() -> loadOrderFromOrderService(orderId));
Future<Customer> customerFuture =
scope.fork(() -> loadCustomerFromDatabase(customerId));
Future<String> invoiceTemplateFuture =
scope.fork(() -> loadInvoiceTemplateFromFile(language));
scope.join();
scope.throwIfFailed();
Order order = orderFuture.resultNow();
Customer customer = customerFuture.resultNow();
String invoiceTemplate = invoiceTemplateFuture.resultNow();
return new Invoice(order, customer, invoiceTemplate);
}
}
使用 StructuredTaskScope,我們可以將 executor.submit()
替換為 scope.fork()
。
使用 scope.join()
,我們等待所有任務完成--或者至少有一個任務失敗或被取消。在後兩種情況下,隨後的 throwIfFailed()
會丟擲一個 ExecutionException
或一個 CancellationException
。
與舊方法相比,新方法帶來了以下改進。
任務和子任務在程式碼中形成一個獨立的單元,每個子任務都在一個新的虛擬執行緒中執行。
一旦其中一個子任務發生錯誤,所有其他子任務都會被取消。
當呼叫執行緒被取消時,子任務也會被取消。
完整程式碼如下:
package git.snippets.jdk19;
import jdk.incubator.concurrent.StructuredTaskScope;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
/**
* 預覽功能
* 控制檯執行
* 1. 設定Java執行環境是JDK 19
* 2. 註釋掉 package 路徑
* 3. 在本程式碼的目錄下執行
* 編譯:javac --enable-preview -source 19 --add-modules jdk.incubator.concurrent StructuredConcurrencyTest.java
*執行:java --enable-preview --add-modules jdk.incubator.concurrent StructuredConcurrencyTest
* @author <a href="mailto:[email protected]">Grey</a>
* @date 2022/9/22
* @since 19
*/
public class StructuredConcurrencyTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
new StructuredConcurrencyTest().createInvoiceSinceJava19(1, 2, "ZH");
}
private final ExecutorService executor = Executors.newCachedThreadPool();
// jdk 19 之前
public Invoice createInvoice(int orderId, int customerId, String language) throws ExecutionException, InterruptedException {
Future<Order> orderFuture = executor.submit(() -> loadOrderFromOrderService(orderId));
Future<Customer> customerFuture = executor.submit(() -> loadCustomerFromDatabase(customerId));
Future<String> invoiceTemplateFuture = executor.submit(() -> loadInvoiceTemplateFromFile(language));
Order order = orderFuture.get();
Customer customer = customerFuture.get();
String invoiceTemplate = invoiceTemplateFuture.get();
return Invoice.generate(order, customer, invoiceTemplate);
}
// jdk 19 之後
public Invoice createInvoiceSinceJava19(int orderId, int customerId, String language)
throws ExecutionException, InterruptedException {
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
Future<Order> orderFuture =
scope.fork(() -> loadOrderFromOrderService(orderId));
Future<Customer> customerFuture =
scope.fork(() -> loadCustomerFromDatabase(customerId));
Future<String> invoiceTemplateFuture =
scope.fork(() -> loadInvoiceTemplateFromFile(language));
scope.join();
scope.throwIfFailed();
Order order = orderFuture.resultNow();
Customer customer = customerFuture.resultNow();
String invoiceTemplate = invoiceTemplateFuture.resultNow();
return new Invoice(order, customer, invoiceTemplate);
}
}
private String loadInvoiceTemplateFromFile(String language) {
return language;
}
private Customer loadCustomerFromDatabase(int customerId) {
return new Customer(customerId);
}
private Order loadOrderFromOrderService(int orderId) {
return new Order(orderId);
}
}
class Invoice {
// TODO
public Invoice(Order order, Customer customer, String invoiceTemplate) {
}
public static Invoice generate(Order order, Customer customer, String invoiceTemplate) {
return null;
}
}
class Order {
private int id;
public Order(int orderId) {
this.id = orderId;
}
}
class Customer {
private int id;
public Customer(int customerId) {
this.id = customerId;
}
}
新的 Vector API與java.util.Vector
類沒有關係。事實上,它是關於數學向量計算的新 API 及其與現代SIMD(單指令-多資料)CPU的對映。
詳見:JDK Enhancement Proposal 426
在 Java SE 19 中,一些函數被標記為 "廢棄 "或無法使用。
在 Java SE 19 中,Locale 類的公共建構函式被標記為 "棄用"。
相反,我們應該使用新的靜態工廠方法Locale.of()
。這可以確保每個 Locale 設定只有一個範例。
下面的例子顯示了與建構函式相比工廠方法的使用情況。
範例程式碼如下
package git.snippets.jdk19;
import java.util.Locale;
/**
* @author <a href="mailto:[email protected]">Grey</a>
* @date 2022/9/22
* @since 19
*/
public class LocaleTest {
public static void main(String[] args) {
Locale german1 = new Locale("de"); // deprecated
Locale germany1 = new Locale("de", "DE"); // deprecated
Locale german2 = Locale.of("de");
Locale germany2 = Locale.of("de", "DE");
System.out.println("german1 == Locale.GERMAN = " + (german1 == Locale.GERMAN));
System.out.println("germany1 == Locale.GERMANY = " + (germany1 == Locale.GERMANY));
System.out.println("german2 == Locale.GERMAN = " + (german2 == Locale.GERMAN));
System.out.println("germany2 == Locale.GERMANY = " + (germany2 == Locale.GERMANY));
}
}
在 Java SE 14 和 Java SE 16 中,有幾個 Thread 和 ThreadGroup 方法被標記為 "被廢棄"
以下這些方法在 Java 19 中已被停用。
ThreadGroup.destroy(); //- 該方法的呼叫將被忽略。
ThreadGroup.isDestroyed() ;//- 總是返回false。
ThreadGroup.setDaemon() ; //- 設定守護者標誌,但這已經沒有效果了。
ThreadGroup.suspend();//會丟擲一個UnsupportedOperationException。
ThreadGroup.resume();//會丟擲一個UnsupportedOperationException。
ThreadGroup.stop();//會丟擲一個UnsupportedOperationException。
所有關於 Java SE 19 的新特性見:JDK 19 Release Notes
本文來自部落格園,作者:Grey Zeng,轉載請註明原文連結:https://www.cnblogs.com/greyzeng/p/16718948.html