Java 17

為什么要了解Java 17?

之所以關心Java 17是因為和Java 8、Java 11一樣它是下一個LTS版本。

image.png

Spring的官宣
image.png

kafka 3.0版本之后也會棄用java 8,升級已經是一個趨勢,未來更多框架和中間件會棄用java 8,作為開發人員也不能停止腳步

使用前的準備

  • IDE升級

檢查 IDE 是否支持 java 17,例如 IDEA 在 file -> project structure -> project -> language level,查看是否支持17,如果最高不支持則需要升級IDEA。

  • 依賴項的升級

  • java 11

java 11刪除了這些原本在jdk中的包:
1.javaFX:JavaFX是取代Swing和AMT,用于開發富GUI(圖形用戶界面)應用的框架
2.刪除java EE和CORBA模塊:SE中刪除java EE相關的包是因為這些包已經由java EE提供,而且由于oracle的政策,一些包的命名空間也改變了,例如JAXB包下的 javax.xml.bind.* 更改為 jakarta.xml.bind.* ,下圖列舉了包名的改動,如果項目使用了這些包,需要在代碼和pom.xml中更改相應包名
...

  • java 14

刪除了CMS GC,對于老項目或針對CMS專門調優過的項目,建議升級后使用G1 GC

  • java 16

java 16對jdk內部的很多api做了強封裝,默認情況下不可訪問(可以通過選項 --illegal-access 更改這個行為,但官方不建議),這個主要影響一些工具,比如lombok,而lombok在java 16發布后不久更新了版本解決這個問題。

  • java 17

1.刪除applet API:Applet 是一種 Java 程序。它一般運行在支持 Java 的 Web 瀏覽器內。因為它有完整的 Java API支持,所以Applet 是一個全功能的 Java 應用程序
2.AOT和JIT被刪除:AOT 編譯是在程序運行之前,便將字節碼轉換為機器碼。JIT是在程序的運行過程中,將字節碼轉換為可在硬件上直接運行的機器碼。


8到17 (累積)語法新特性

  • 新增類型推斷
import java.util.ArrayList;
import java.util.List;

public class Var {

    // var a = "a";

    void get() {
        var b = "b";

        // 泛型為Object
        var list = List.of("a", 1);
        var list2 = new ArrayList<>();
        list2.add("a");
        list2.add(1);

        // 無法推斷匿名類
        Runnable runnable = () -> System.out.println("interface var");
        // var runnable2 = () -> System.out.println("interface var");
    }
}

  • Stream 增強
import java.util.*;
import java.util.stream.Stream;

public class StreamJava {
    // max 10
    Map<String, Integer> map = Map.of("a", 1, "b", 2, "c", 3);

    // List<String> list = List.of("a", 1);


    // toList()
    List<Integer> list = Stream.of(1, 2, 3).toList();

    // 多元素替換一個元素
    public static void main(String[] args) {
        Stream.of(1, 2, 3)
                //.flatMap(num -> Stream.of(num + num, num * num, " "))
                .mapMulti((num, downstream) -> {
                    downstream.accept(num + num);
                    downstream.accept(num * num);
                    downstream.accept(" ");
                })
                .forEach(System.out::println);
    }

    // optional
    long count = Stream.of(
            Optional.of(1),
            Optional.empty(),
            Optional.of(2)).flatMap(Optional::stream).count();

}

  • instance of 增強
public class InstanceOf {
    void get() {
        Object obj = "string value";
        //傳統寫法
        if (obj instanceof String) {
            String str = (String) obj;
            if (str.contains("val")) {
                
            }
        }

        //新寫法
        if (obj instanceof String s) {
        }

        //還可以做其他操作
        if (obj instanceof String s && s.contains("val")) {
        }
    }
}

  • 接口支持 private 方法
public interface Interface {
    private void get() {
        System.out.println();
    }
}

  • 新增封閉類
public sealed class Sealed permits A {

}

final class A extends Sealed {} // OK
// final class B extends Sealed {} // error

  • switch 語句增強
public class Switch {

    public void switchNowTest() {
        var c = 'B';
        var res = switch (c) {
            case 'A' -> "優秀";
            case 'B' -> "良好";
            case 'C' -> "及格";
            default -> "未知等級";
        };
    }

    public void formatTest() {
        Object o = 100;
        String formatted = switch (o) {
            //相當于 if (o instanceof Integer && (int)o > 10)
            case Integer i && i > 10 -> String.format("a large Integer %d", i);
            case Integer i -> String.format("a small Integer %d", i);
            case Long l -> String.format("a Long %d", l);
            default -> o.toString();
        };
        System.out.println(formatted);
    }

}

  • try 語句增強
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;

public class Try {
    void get() {
        var reader = new InputStreamReader(System.in);
        var writer = new OutputStreamWriter(System.out);
        try (reader; writer) {
            //reader是final的,不可再被賦值
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

  • 新增文本塊
public class Text {
//    String a = """abc
//            """;


    static String text = """
            abc
            b
            c
                def
            """;

    static String text2 = """
            abc
            b
            c
                def
ghi
            """;

    static String text3 = """
            {
                "name": "abc",
                "age": 123
                /
                //
                \n
            }
            """;

    public static void main(String[] args) {
        System.out.println(Text.text2);
    }
}

  • 新增 record 基本引用類型
public record Record(String name, Integer age) {

    // public int a;

//    Record(int a) {
//        this.a = a;
//    }

    // public static String b = "b";

    public Record {
        if (age < 0) {
            throw new RuntimeException("年齡不正確");
        }
        if (age > 100) {
            age = 100;
        }
    }

    public static void main(String[] args) {
        Record a = new Record("a", 200);
        System.out.println(a);
        System.out.println(a.age);
        // System.out.println(Record.b);
    }
}

  • 新增 http client 工具
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;

public class Http {

    HttpClient client;


    public void init() {
        client = HttpClient.newBuilder().connectTimeout(Duration.ofMillis(20000L)).build();
    }

    public void reqTest() throws IOException, InterruptedException {
        var request = HttpRequest.newBuilder(URI.create("https://baidu.com/")).build();
        /**
         * 沒有指定協議默認是 GET
         */
        String body = client.send(request, HttpResponse.BodyHandlers.ofString()).body();
        System.out.println(body);
    }

    public void getTest() {
        var request = HttpRequest.newBuilder()
                .uri(URI.create("https://baidu.com/"))
                .header("Cookie", cookie)
                .timeout(Duration.ofSeconds(10000L))
                .GET()
                .build();
        client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
                .whenCompleteAsync((res, exp) -> {
                    System.out.println(res.body());
                }).join();
    }


    public void postTest() {
        var requestBody = "{'key':'val'}";
        var request = HttpRequest.newBuilder()
                .uri(URI.create("http://baidu.com/"))
                .header("Contend-Type","application/json")
                .timeout(Duration.ofSeconds(10000L))
                .POST(HttpRequest.BodyPublishers.ofString(requestBody))
                .build();
        client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
                .whenCompleteAsync((res, exp) -> {
                    System.out.println(res.body());
                }).join();
    }


    public void Http2Test() throws URISyntaxException {
        HttpClient.newBuilder()
                .followRedirects(HttpClient.Redirect.NEVER)
                .version(HttpClient.Version.HTTP_2)
                .build()
                .sendAsync(HttpRequest.newBuilder()
                                .uri(new URI("https://baidu.com/"))
                                .GET()
                                .build(),
                        HttpResponse.BodyHandlers.ofString())
                .whenComplete((resp, t) -> {
                    if (t != null) {
                        t.printStackTrace();
                    } else {
                        System.out.println(resp.version());
                        System.out.println(resp.statusCode());
                    }
                }).join();
    }

}


其他重要更新

  • 恢復始終嚴格的浮點語義(曾需使用strictfp關鍵字)

過于嚴格的浮點計算在當初流行的 x86 架構和 x87 浮點協議處理器上運行,需要大量的額外的指令開銷,所以java1.2之后去掉嚴格浮點,使用strictfp關鍵字
但是非嚴格浮點數計算會導致不同平臺的計算結果可能不一致


image.png
  • 使用新的 macOS 渲染庫,支持 macOS/AArch64 架構

    ···

  • 指定上下文的反序列化過濾器

反序列化危險的一個原因是,有時候我們不好驗證將要進行反序列化的內容是否存在風險,而傳入的數據流可以自由引用對象,很有可能這個數據流就是攻擊者精心構造的惡意代碼。

public class JEP415 {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        Dog dog = new Dog("哈士奇");
        dog.setPoc(new Poc());
        // 序列化 - 對象轉字節數組
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        try (ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);) {
            objectOutputStream.writeObject(dog);
        }
        byte[] bytes = byteArrayOutputStream.toByteArray();
        // 反序列化 - 字節數組轉對象
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
        ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
        // 允許 com.wdbyte.java17.Dog 類,允許 java.base 中的所有類,拒絕其他任何類
        ObjectInputFilter filter = ObjectInputFilter.Config.createFilter(
                        "com.wdbyte.java17.Dog;java.base/*;!*");
        objectInputStream.setObjectInputFilter(filter);
        Object object = objectInputStream.readObject();
        System.out.println(object.toString());
    }
}

class Dog implements Serializable {
    private String name;
    private Poc poc;

    public Dog(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Dog{" + "name='" + name + '\'' + '}';
    }
        // get...set...
}

class Poc implements Serializable{
}


GC優化 - G1收集器

  • 簡介

G1收集器最早出現在Java 7中,但需要手動開啟,從Java 9開始將G1收集器作為默認垃圾收集器,以取代CMS收集器。
G1垃圾收集器可以給你設定一個你希望Stop the world停頓時間,G1會根據這個時間盡量滿足你。

  • 內存模型

在G1垃圾收集器中,堆的劃分不再是物理形式,而是以邏輯的形式進行劃分


image.png
  • GC過程
image.png
  • Minor GC
    掃描、處理跨Region引用,收集至CSet,復制清除、處理引用
  • Mixed GC
    當堆空間的占用率達到一定閾值后會觸發Mixed GC(默認45%,由參數決定)。Mixed GC會依賴全局并發標記統計后的Region數據
    初始標記(STW)、并發標記、最終標記(STW)、清理(STW)
  • Full GC
    特殊場景會發生full GC

  • 性能
    性能測試
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容