Java新特性(2):Java 10以後

2022-11-18 09:00:20

您好,我是湘王,這是我的部落格園,歡迎您來,歡迎您再來~

 

雖然到目前為止Java的版本更新還沒有什麼驚天動地的改變,但總是會冒出一些有趣的小玩意。前面列舉了Java9Java10的一些特色,現在接著來擼一擼Java11之後的新奇特。

Java9更新了Http 2 Client,也說過先不著急看,因為在後續版本中語法會變。這不,Java11就實現了。最直接的變化就是http相關包名由Java 9的jdk.incubator.http改為Java 11的java.net.http。感覺java.net.http才像那麼回事,incubator是個啥呢?好像完全和http不沾邊。本著好奇害死貓的精神,查了下incubator的意思:

 

 

 

 

好吧,原來JDK工作組認為http在Java9中出現是個「早產兒」。

再來看看Java11http的更新,例如通過http存取某度的主頁:

// 包名由Java 9的jdk.incubator.http改為Java 11的java.net.http
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
                                .uri(URI.create("http://www.baidu.com/"))
                                .build();
client.sendAsync(request, BodyHandlers.ofString())
                                .thenApply(HttpResponse::body)
                                .thenAccept(System.out::println)
                                .join();

 

 

現在Lambda表示式幾乎無處不在了,如果還不會的話搞不好以後Java程式碼都看不懂了)

 

Java11有如下變更:

 

 

 

除了http之外,比較有用的就兩個:基於巢狀的存取控制(181)和飛行記錄器(328)。

所謂基於巢狀的存取控制,其實就是能夠判斷某個類是不是另一個類的巢狀類。

/**
 * 基於巢狀的存取控制
 * 
 * @author 湘王
 */
public class OuterClass {
    class InnerClass {
        public InnerClass() {
        }

        public void printOuterInt() {
            System.out.println("InnerClass");
        }
    }

    public static void main(String[] args) {
        Class<?> outerClass = OuterClass.class;
        // 得到宿主類
        Class<?> clazz1 = InnerClass.class.getNestHost();
        System.out.println(clazz1.getName());
        // 得到內部類成員
        Class<?>[] clazz2 = OuterClass.class.getNestMembers();
        for (Class<?> class1 : clazz2) {
            System.out.println(class1.getName());
            // 判斷類是否是某個類的巢狀類
            System.out.println(outerClass.isNestmateOf(class1));
        }
    }
}

 

 

就是這樣子用的。

所謂飛行記錄器,就是模仿飛機上的黑匣子,是一種低開銷的事件資訊收集框架,它原來是JDK商業版(是一種大廠之間合作收費的版本)中的一項分析工具,主要資料來源於應用程式、JVM和OS,是在故障發生後,能夠從事件記錄檔案中提取出有用資訊對故障進行分析。到了Java11,它索性就免費了。

/**
 * 飛行記錄器
 *
 * @author 湘王
 */
public class FlightRecorder {
    @Label("Hello World")
    @Description("Helps the programmer getting started")
    static class HelloWorld extends Event {
        @Label("Message")
        String message;
    }

    public static void main(String[] args) {
        HelloWorld event = new HelloWorld();
        event.message = "hello, world!";
        event.commit();
    }
}

 

執行上述程式碼時加上JVM引數:

-XX:StartFlightRecording=duration=1s, filename=C:\\recording.jfr

 

然後再通過飛行記錄器來讀取資料:

// 讀取飛行記錄資料
final Path path = Paths.get("C:\\recording.jfr");
List<RecordedEvent> recordedEvents;
try {
    recordedEvents = RecordingFile.readAllEvents(path);
    for (RecordedEvent event : recordedEvents) {
        System.out.println(event.getStartTime() + "," + event.getValue("message"));
    }
} catch (IOException e) {
    e.printStackTrace();
}

 

 

至於Java11中的其他更新項:

1、Epsilon GC(no-op GC,318)

2、ZGC可伸縮低延遲垃圾收集器(333)

3、支援TLSv1.3協定(332)

4、動態類檔案常數(309)

我個人覺得沒啥特別的。

 

Java11之後,感覺Java是為了更新而更新,新奇特不多。差不多每個版本多那麼一個小玩意。

這是Java12的更新:

 

 

 

Java12多了一個switch表示式(325):

@SuppressWarnings("preview")
public static void main(String[] args) {
    DayOfWeek day = LocalDate.now().getDayOfWeek();

    int number = switch (day) {
        case MONDAY, WEDNESDAY, FRIDAY, SUNDAY -> 1;
        case TUESDAY, THURSDAY, SATURDAY -> 2;
    };
    System.out.println(number);

    switch (day) {
        case MONDAY:
            System.out.println("星期一");
            break;
        case TUESDAY:
            System.out.println("星期二");
            break;
        case WEDNESDAY:
            System.out.println("星期三");
            break;
        case THURSDAY:
            System.out.println("星期四");
            break;
        case FRIDAY:
            System.out.println("星期五");
            break;
        case SATURDAY:
            System.out.println("星期六");
            break;
        case SUNDAY:
            System.out.println("星期日");
            break;
    }
}

 

 

Java13整體上與Java12差不多,沒什麼變化:

 

 

 

唯一比較引人注目的就是模仿Python搞了一個三引號的文字塊(而且之前剛出來的時候還只能在idea中實驗):

 

 

然後再敲程式碼:

@SuppressWarnings("preview")
public static void main(String[] args) {
    // 文字塊
    String text = """
    Lorem ipsum dolor sit amet, consectetur adipiscing
    elit, sed do eiusmod tempor incididunt ut labore
    et dolore magna aliqua.
    """;
    System.out.println(text);
}

 

 

Java14Java13基礎上就更新的比較多了:

 

 

 

 

其中有一個可能會比較有用的特性NullPointerExceptions增強(358):

例如碼農可能會寫這樣的程式碼:

通常會有這樣的程式碼:

User user = new User();

String cityname = user.getDetailInfo().getAddress().getCity().getName();

System.out.println(cityname);

在呼叫過程中,如果User、DetailInfo、Address、City中有任何一個是null,那麼就會丟擲NullPointerExceptions,但是比較難於確定倒底是哪一個物件為null。列印出來的異常資訊是:

 

 

 

但在Java14中,卻可以很準確地知道NPE發生在哪裡:

先通過JVM引數開啟這項特性:-XX:+ShowCodeDetailsInExceptionMessages

 

 

 

再次執行相同的程式碼,可以看到列印出的異常資訊變了:

 

 

 

 

不過有個條件:如果程式碼中已有捕獲的NullPointerExceptions,那麼就不會執行異常計算。也就是說如果使用了try...catch的話,那這項特性就沒用了。

final class Point {
    public final int x;
    public final int y;
    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }
    // state-based implementations of equals, hashCode, toString
    // nothing else
}

 

Java14另一個比較有用的特性是Record型別。對,你沒看錯,不是類,是型別,是和Integer、String一樣的型別,Record。那什麼是Record型別呢?

以前在定義一個JavaBean的時候,通常都會附帶定義一些建構函式、getter/setter、equals()、hashCode()以及toString()等無用且無聊的程式碼。所以出現了第三方類庫Lombok,它就可以自動生成這些程式碼。例如:

 

Record型別就是為了定義這種「純資料載體」而生的:

 

record Name(String firstname, String lastname) { }

record Address(String... address) { }

record User(Name name, Address address, int age, ...) {
    static int x;
}

 

 

Java15Java14基礎上又做了一系列更新:

 

 

 

Java15比較突出的是增加一個稱之為「封印類」(360,特性編號也是360,不會是巧合吧)的安全類。所謂封印類,其實就是告訴外界,只有哪些類或介面可以繼承或實現它。

// 這段程式碼宣告了一個Shape介面,而permits列表限制了只有「Circle」和「Rectangle」可以實現Shape
// 任何其他嘗試擴充套件Shape的類或介面都將收到編譯錯誤
sealed interface Shape permits Circle, Rectangle {
}

 

 

封印類常常和record一起使用:

// 只有「Circle」和「Rectangle」可以實現Shape
// 「Circle」需要一個座標和半徑才能確定
// 「Rectangle」需要兩個座標就能確定
sealed interface Shape permits Circle, Rectangle {
    record Circle(Point center, int radius) implements Shape { }
    record Rectangle(Point lowerLeft, Point upperRight) implements Shape { }
}

 

 

 

後續的Java更新就不再囉嗦了。還是之前的那個態度:Java8是一個非常重要的分水嶺,如果不把Lambda表示式和函數語言程式設計學好,後面可能會有很多騷操作都做不了了。

 

 


 

 

感謝您的大駕光臨!諮詢技術、產品、運營和管理相關問題,請關注後留言。歡迎騷擾,不勝榮幸~