我還在生產玩 JDK7,JDK 15 卻要來了!|新特性嚐鮮
阿新 • • 發佈:2020-08-05
![](https://img2020.cnblogs.com/other/1419561/202008/1419561-20200805073515465-1087956696.jpg)
自從 JDK9 之後,每年 3 月與 9 月 JDK 都會發佈一個新的版本,而2020 年 9 月即將引來 JDK15。
恰巧 IDEA 每四五個月會升級一個較大的版本,每次升級之後都會支援最新版本 JDK 引入的新功能。
這幾天升級了 IDEA,順便體驗了一下 JDK15 的新特性。
雖然我知道你們可能跟我一樣JDK8 都還沒用熟,但是無妨,看看新版本 JDK 來酸一下。
![](https://img2020.cnblogs.com/other/1419561/202008/1419561-20200805073515744-685675113.jpg)
## Text Blocks 最終定板
之前版本的 JDK,如果我們需要插入 **HTML**,**XML**,**SQL** 或 **JSON** 片段,非常麻煩,需要對裡面符號進行各種轉義。
所以我每次都會在其他編輯器將 **HTML** ,**XML** 等編輯好,然後直接複製到 IDEA 中,IDEA 自動會對這些字元轉義。
![](https://img2020.cnblogs.com/other/1419561/202008/1419561-20200805073516164-2125141226.jpg)
每次複製進去就變成上圖的效果,如果上面字元再多點,閱讀起來就會更難,並且難以維護。
所幸 IDEA 提供了一個 **Inject Language** 功能,我們可以在裡面快速方便的編輯。
![](https://img2020.cnblogs.com/other/1419561/202008/1419561-20200805073516357-1063118793.jpg)
Java 開發者也關注到這個問題,他們在 JDK13 引入的一個新的預覽特性「**Text Blocks**」,可以使用三引號將複雜的字串賦值,從而讓我們從各種轉義中解脫出來,可以更加方便的編輯字串。
這個功能在其他語言還是比較常見的,比如 Python 等。
**Text Blocks** 新功能在 JDK14 再次以預覽功能引入,最終在 JDK15 成為新版本的正式功能。
下面我們來對比一下使用 **Text Blocks** 與之前區別:
![Html](https://img2020.cnblogs.com/other/1419561/202008/1419561-20200805073516547-1513648087.jpg)
![SQL](https://img2020.cnblogs.com/other/1419561/202008/1419561-20200805073516881-529596295.jpg)
![JS](https://img2020.cnblogs.com/other/1419561/202008/1419561-20200805073517333-839006147.jpg)
## Records (Second Preview)
JDK14 引入一個新的預覽特性 **record** 語法,可以快速建立一個純資料類,並且不用去生成 **getter**,**toString** 等。
使用下面的語法就可以快速建立一個數據類:
```java
public record Point(int x,int y) {
}
```
JDK15 是 **record** 這個語法的第二次預覽,這個版本增加一個新的功能 「**local record**」,可以在一個方法在快速建立一個類,以便於方法中業務邏輯計算。
在以下示例中,使用本地記錄 **MerchantSales** 對商人和每月銷售額的彙總進行建模,使用此記錄可提高以下流操作的可讀性:
下面例子的中我們新建一個類 **MerchantSales**,然後按照銷售人員對每月的銷售額彙總排序。
```java
List findTopMerchants(List merchants, int month) {
// Local record
record MerchantSales(Merchant merchant, double sales) {}
return merchants.stream()
.map(merchant -> new MerchantSales(merchant, computeSales(merchant, month)))
.sorted((m1, m2) -> Double.compare(m2.sales(), m1.sales()))
.map(MerchantSales::merchant)
.collect(toList());
}
```
原先如果需要使用這種功能,我們不得不建立一個內部類,後續可能再也不會用到,使用 **local record**就解決這個尷尬的問題。
除了 **local record** 我們還可以建立 **local enums** 以及 **local interface**。
```java
// local enums
public void organisePeople(List people) {
enum Role {
Employee, Customer, Both, None
}
HashMap> peopleByRole = new HashMap<>();
people.stream()
.filter(Person::isCustomer)
.forEach(person -> peopleByRole.computeIfAbsent(Role.Customer, role -> new ArrayList<>())
.add(person));
// 其他業務邏輯
}
```
```java
// local interface
public void localInterface() {
interface MyInterface {
void doSomething();
}
MyInterface testInterface = new MyInterface() {
@Override
public void doSomething() {
System.out.println("Hello World!");
}
};
// 其他業務邏輯
}
```
最後使用這個特性需要注意一點,**local record** , **local enums** ,**local interface** 建立都是一個區域性變數,是不能被傳遞其他方法引用。
## Pattern Matching for instanceof (Second Preview)
我們應該都看到過下面這種程式碼:
```java
if (obj instanceof String) {
String str = (String) obj;
// use str
}
```
上面程式碼意圖非常簡單,當 **obj** 物件是 **String** 類,就將其強制轉換,然後進行其他業務操作。
這種寫法,型別轉換還是比較繁瑣,**Pattern Matching for instanceof** 這個新語法特性,可以幫我們省略這種型別轉換動作。這是一個在 JDK14 引入一個預覽特性,JDK 15 開始第二次預覽。
上面的程式碼使用 **pattern matcher**,就可以被修改如下:
```java
if (obj instanceof String s) {
s.contains("T");
} else {
// 編譯錯誤
//s.contains("T");
}
```
另外如果在 IDEA 中還可以提示我們將程式碼轉化成 **pattern matcher** 。
![](https://img2020.cnblogs.com/other/1419561/202008/1419561-20200805073517849-1077149039.gif)
大家應該都看過 **Effective Java** 這本神書吧,裡面第八條關於 **Equals** 有一個例子:
![](https://img2020.cnblogs.com/other/1419561/202008/1419561-20200805073518288-889639498.jpg)
使用 **pattern matcher** 我們就可以使用下面更加清晰的程式碼代替:
![](https://img2020.cnblogs.com/other/1419561/202008/1419561-20200805073518599-2052107338.jpg)
## Sealed Classes (Preview)
Java 中一個正常普通類/介面允許被其他子類繼承/實現,但是有時在日常開發中,我們可能希望只有特定的類才能繼承擴充套件。
現有的 Java 語法中存在一些方法,可以限制子類擴充套件,比如說:我們可以使用 **final** 修飾類
```java
public final class String
```
不過這樣之後,我們就沒辦法再繼承這個類。
其次我們可以限制的類的範圍,比如說不使用 **public** 修飾類/介面,即使用 **default** 範圍,這樣只有同一個包才能繼承/實現。
```java
interface DefaultExample {
}
```
不過使用這種方式,又很尷尬,這個類就無法被其他包使用。
為了解決上述問題,JDK 15 引入一個新的預覽特性 **Sealed Classes**,即可以限定類的擴充套件,也可以被外部使用。
```java
public sealed class Shape
permits Circle, Rectangle, Square {...}
```
使用 **sealed** 修飾之後,`Shape` 類只能被 `Circle`,`Rectangle`,`Square`繼承,再也不能被其他類繼承。
同時 **Shape** 的子類存在一些限制,必須使用 **final** 修飾,表明這個類無法再被擴充套件:
```java
public final class Circle extends Shape {...}
```
或者繼續使用 **sealed** 表示子類只能被指定類繼承:
```javascript
public sealed class Rectangle extends Shape
permits TransparentRectangle, FilledRectangle {...}
public final class TransparentRectangle extends Rectangle {...}
```
又或者說使用 **non-sealed** 表明這個子類不限制子類擴充套件,可以被其他任何類擴充套件實現。
另外 **sealed class** 還可以跟上述 **record** 語法一起使用。
```java
public sealed interface Expr
permits ConstantExpr, PlusExpr, TimesExpr, NegExpr {...}
public record ConstantExpr(int i) implements Expr {...}
public record PlusExpr(Expr a, Expr b) implements Expr {...}
public record TimesExpr(Expr a, Expr b) implements Expr {...}
public record NegExpr(Expr e) implements Expr {...}
```
## ZGC
**ZGC(Z Garbage Collector)** 這是一款在 JDK11 引入的的具有實驗性質的低延遲的 GC 收集器。
這款 GC 收集器的希望在儘可能對吞吐量影響不大的前提下,實現在任意堆記憶體大小都可以把垃圾收集器的停頓時間限制在十毫秒以內的低延遲。
**ZGC** 經過這兩三年的迭代優化,終於在 JDK15 中正式引入,標誌著 **ZGC** 可以正式應用於生產應用。
JDK15 中預設虛擬機器還是 **G1**,如果需要使用 ZGC,需要在啟動引數中加入如下引數:
```shell
-XX:+UseZGC command-line
```
## 最後
本來這篇文章是準備寫下 IDEA 2020.2 新版本特性,順帶介紹一下 JDK15 新特性的。
可是沒想到寫著寫著,JDK15 相關的篇幅就過長了,所以就單獨拿出來了。
最後,最後,JDK 都發布到 15 了,而我卻還在用 JDK 7 ,真是個悲傷的故事,逃了逃了!
![](https://img2020.cnblogs.com/other/1419561/202008/1419561-20200805073518782-282728987.jpg)
我是樓下小黑哥,每天學習一點點,成長億點點!!
### 參考連結
https://openjdk.java.net/projects/jdk/15/
> 歡迎關注我的公眾號:程式通事,獲得日常乾貨推送。如果您對我的專題內容感興趣,也可以關注我的部落格:[studyidea.cn](https://studyidea.cn)