jdk17新特性梳理

jdk8升級至jdk17新特性梳理

因爲公司後續將採用jdk17進行開發,顧進行一個jdk17新特性的簡單梳理

升級jdk17的理由

最簡單的理由就是,springboot3.0放棄了jdk8,kafka4.0同樣放棄了jdk8

新特性梳理

可以在接口中定義私有方法,主要爲了jdk8的default方法

public interface PricateMethodTest{
    default void defaultMethod(){
        privateMethod();
    }

    private void privateMethod(){

    }
}

局部變量可以使用var來定義

// jdk8的寫法
String a1 = "111";
// jdk10之後的寫法
var a2 = "222";

Http客戶端鏈式操作

public class Test{
    public static void main(String[] args){
        var httpClient = HttpClient.newBuilder()
            .version(HttpClient.Version.HTTP_1_1)
            .connectTimeout(Duration.ofSeconds(10))
            .build();
        var resuest = HttpClient.newBuilder()
            .GET()
            .uri(URI.create("www.baidu.com"))
            .setHeader("User-Agent","Google")
            .build();
    }
}

提供了原生的websocket相關api(這塊暫無測試)

主要得益於SocketAPI 重構,Socket的底層實現優化,引入了NIO

空指針問題

在jdk14之前,異常捕獲後拋出信息只有java.lang.NullPointerException
測試代碼

public static void main(String[] args) {
        try {
            List<Integer> list = new ArrayList<>();
            list.add(null);
            System.out.println(list.get(0).longValue());
        }catch (Exception e){
            e.printStackTrace();
        }
    }

在jdk14之後,會顯示具體的空指針對象,具體返回如下:

java.lang.NullPointerException: Cannot invoke "java.lang.Integer.longValue()" because the return value of "java.util.List.get(int)" is null

switch改造

jdk17之前版本採用了break返回的方式

public static int testBreak(String mode){
    int i = 0;
    switch(mode){
        case "a","b":
            i = 1;
            break;
        case "c":
            i = 2;
            break;
        default:
            i = 3;
            break;
    }
    return i;
}

jdk14之後採用了yield

public static void testYield(String mode){
    int result = switch(mode){
        case "a","b":
            yield 1; // 使用yield提供返回值
        case "c":
            yield 2; // 使用yield提供返回值
        default:
            yield 3; // 使用yield提供返回值
    }
    return result;
}

或者直接建圖返回結果

public static void testYield(String mode){
    int result = switch(mode){
        case "a","b" -> 1;
        case "c" -> 2;
        default -> 3;
    }
    return result;
}

文本塊的引入

// jdk1.8的寫法
String html = "<html>\n" +
            "   <head>\n" +
            "       <title>test</title>\n" +
            "   </head>\n" +
            "   <body>\n" +
            "       <h1>test</h1>\n" +
            "   </body>\n" +
            "  </html>\n";
// jdk13的寫法
var jdk15Html = """
                <html>
                    <body>
                        <p>hello, world</p>
                    </body>
                </html>
                """;

instanceof的變化

jdk8是使用instanceof關鍵字判斷類型之後,再強轉,示例代碼如下

// jdk8
if (obj instanceof String) {
    String s = (String) obj;
    if (s.equals("1")) {
        System.out.println("1");
    }
}
// jdk16,則直接可在if中完成操作,str爲申明的變量
if (obj instanceof String str && str.equals("1")) {
    System.out.println("1");
}

申明對象時可用record關鍵字實現get set方法

public record User(long id,String name,int age){

}

提供了VarHandle

jdk9之後提供的VarHandle時對Unsafe的一個優化,jdk9之前的Unsafe是不建議開發者直接使用的,因爲Unsafe所操作的並不屬於Java標準,會容易帶來一些安全性的問題

什麼是VarHandle

Varhandle是對變量或參數定義的變量系列的動態強類型引用,包括靜態字段,非靜態字段,數組元素或堆外數據結構的組件。 在各種訪問模式下都支持訪問這些變量,包括簡單的讀/寫訪問,volatile 的讀/寫訪問以及 CAS (compare-and-set)訪問。簡單來說 Variable 就是對這些變量進行綁定,通過 Varhandle 直接對這些變量進行操作。

舉例

public class Demo {
   public int publicVar = 1;
   protected int protectedVar = 2;
   private int privateVar = 3;
   public int[] arrayData = new int[]{1, 2, 3};
   @Override
   public String toString() {
       return "Demo{" +
               "publicVar=" + publicVar +
               ", protectedVar=" + protectedVar +
               ", privateVar=" + privateVar +
               ", arrayData=" + Arrays.toString(arrayData) +
               '}';
   }
}
// 訪問private成員
private static void privateDemo() throws NoSuchFieldException, IllegalAccessException {
   Demo instance = new Demo();
   VarHandle varHandle = MethodHandles.privateLookupIn(Demo.class, MethodHandles.lookup())
           .findVarHandle(Demo.class, "privateVar", int.class);
   varHandle.set(instance, 33);
   System.out.println(instance);
}

輸出:Demo

// 訪問 protected 成員
private static void protectedDemo() throws NoSuchFieldException, IllegalAccessException {
    Demo instance = new Demo();

      VarHandle varHandle = MethodHandles.privateLookupIn(Demo.class,MethodHandles.lookup())
              .findVarHandle(Demo.class, "protectedVar", int.class);

    VarHandle varHandle = MethodHandles.lookup()
            .in(Demo.class)
            .findVarHandle(Demo.class, "protectedVar", int.class);
    varHandle.set(instance, 22);
    System.out.println(instance);
}

輸出:Demo

// 訪問public成員
private static void publicDemo() throws NoSuchFieldException, IllegalAccessException {
     Demo instance = new Demo();
     VarHandle varHandle = MethodHandles.lookup()
             .in(Demo.class)
             .findVarHandle(Demo.class, "publicVar", int.class);
     varHandle.set(instance, 11);
     System.out.println(instance);
 }

輸出:Demo

// 訪問數組
private static void arrayDemo() throws NoSuchFieldException, IllegalAccessException {
    Demo instance = new Demo();
    VarHandle arrayVarHandle = MethodHandles.arrayElementVarHandle(int[].class);
    arrayVarHandle.compareAndSet(instance.arrayData, 0, 1, 11);
    arrayVarHandle.compareAndSet(instance.arrayData, 1, 2, 22);
    arrayVarHandle.compareAndSet(instance.arrayData, 2, 3, 33);
    System.out.println(instance);
}

輸出:Demo

Varhandle方法彙總

  • MethodHandles.privateLookupIn(class, MethodHandles.lookup())獲取訪問私有變量的Lookup
  • MethodHandles.lookup() 獲取訪問protected、public的Lookup
  • findVarHandle:用於創建對象中非靜態字段的VarHandle。接收參數有三個,第一個爲接收者的class對象,第二個是字段名稱,第三個是字段類型。
  • findStaticVarHandle:用於創建對象中靜態字段的VarHandle,接收參數與findVarHandle一致。
  • unreflectVarHandle:通過反射字段Field創建VarHandle。
  • MethodHandles.arrayElementVarHandle(int[].class) 獲取管理數組的 Varhandle

內存屏障

VarHandle 除了支持各種訪問模式下訪問變量之外,還提供了一套內存屏障方法,目的是爲了給內存排序提供更細粒度的控制。主要如下幾個方法:

public static void fullFence() {
    UNSAFE.fullFence();
}
public static void acquireFence() {
    UNSAFE.loadFence();
}
public static void releaseFence() {
    UNSAFE.storeFence();
}
public static void loadLoadFence() {
    UNSAFE.loadLoadFence();
}
public static void storeStoreFence() {
    UNSAFE.storeStoreFence();
}

小結

在 java9 之後,對一些變量的併發操作時,可以考慮用 java.lang.invoke.VarHandle 來處理,而不是通過 Unsafe 類來處理,畢竟 Unsafe 不太適合直接使用。

Sealed Classes(封閉類)

  • 密封Sealed表示的概念是可以擴展一個類,但只能通過已知的子類型列表進行擴展,而不能通過其他任何擴展。
  • 其他語言可能對功能的看法有所不同,在Java中,應將其視爲代表幾乎最終類的功能。
  • Java 16開始,允許使用 sealed 修飾class,並通過permits明確寫出能夠從該class繼承的子類名稱。
// 測試代碼
public sealed class Car permits MiniCar, SmallCar, Bigcar{
    ...
}
// 以下代碼可以正常運行
public final class MiniCar extends Car {...}
// 以下代碼會報錯
public final class Train extends Car {...}

其他新特性

其他新特性有待於各位小夥伴努力嘗試
來源:不曉得哪裏抄的

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章