為什么要了解Java 17?
之所以關心Java 17是因為和Java 8、Java 11一樣它是下一個LTS版本。
Spring的官宣
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關鍵字
但是非嚴格浮點數計算會導致不同平臺的計算結果可能不一致
-
使用新的 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垃圾收集器中,堆的劃分不再是物理形式,而是以邏輯的形式進行劃分
-
GC過程
-
Minor GC
掃描、處理跨Region引用,收集至CSet,復制清除、處理引用 -
Mixed GC
當堆空間的占用率達到一定閾值后會觸發Mixed GC(默認45%,由參數決定)。Mixed GC會依賴全局并發標記統計后的Region數據
初始標記(STW)、并發標記、最終標記(STW)、清理(STW) -
Full GC
特殊場景會發生full GC
-
性能
性能測試