Java8 之后,Java 又發(fā)布了幾十個新特性,看看有沒有讓你驚艷的!
大家好,我是君哥。
雖然多數(shù)人還是使用 Java 8,但并沒有阻擋 Java 更新的腳步,最近 Java 23 發(fā)布了。今天來聊一聊 Java 8 到 java 23 增加了哪些新特性。
Java 9
- 私有接口方法。
- 默認垃圾收集器改為 G1。
- HTTP client,支持 WebSocket、HTTP/2、HTTPS/TLS、非阻塞 API。
Java 10
- 局部變量類型推斷,可以使用 var 類型來定義變量。
- 不可變集合。
- G1 支持并行 Full GC。
- 基于 Java 的 JIT 編譯器 Graal。
- 支持在不執(zhí)行全局安全點的情況下執(zhí)行線程回調(diào),這樣可以在不停止所有線程的情況下停止單個線程。
Java 11
- 標準 HTTP Client 升級。
- 引入 ZGC 垃圾收集器。
- Flight Recorder,可以收集基于 OS、JVM和JDK 事件產(chǎn)生的數(shù)據(jù)。
- 對Stream、Optional、集合 API進行增強。
Java 12
- 引入 Switch 表達式。
- Shenandoah GC 垃圾收集算法。
- JMH 基準測試。
- G1 支持可中斷的 mixed GC,將 Mixed GC 拆分為強制部分和可選部分,強制部分一定會被回收,可選部分可以不被回收,這樣垃圾收集過程中優(yōu)先處理強制集,更容易滿足暫停時間目標。
- G1 可以歸還不使用的內(nèi)存給操作系統(tǒng)
Java 13
- switch 優(yōu)化更新,增加 yield 關(guān)鍵字用于返回結(jié)果。
- ZGC 支持將未使用的內(nèi)存歸還操作系統(tǒng)。
- 引入了文本塊,可以使用 """ 三個引號表示文本塊,示例代碼如下:
String html = """
<html>
<body>
<p>Hello, world</p>
</body>
</html>
""";
Java 14
- instanceof 語法簡化,可以直接給對象賦值:
if (obj instanceof String s) {
//這里可以使用 s 變量
} else {
//這里不能使用 s 變量
}
- 引入 Record,類似于枚舉類型,具有 Lombok 功能,可以自動生成構(gòu)造器、equals、getter 等方法。
- 放棄 CMS。
Java 15
- 引入 hidden class。
- String.substring 優(yōu)化,如果長度為 0,返回 null。
- 引入 Sealed class。
Java 16
- Stream新增toList方法。
- 提供jpackage。
- java.time 根據(jù)時段獲取時間。
Java 17
- 升級 switch 使用,switch可直接用 instanceof 模式匹配選擇,不過需要提前做 null 判斷(下面代碼選自 oschina):
Object o;
switch (o) {
case null -> System.out.println("首先判斷對象是否為空,走空指針邏輯等后續(xù)邏輯");
case String s -> System.out.println("判斷是否為字符串,s:" + s);
case record p -> System.out.println("判斷是否為Record類型: " + p.toString());
case int[] arr -> System.out.println("判斷是否為數(shù)組,展示int數(shù)組的長度" + ia.length);
case Integer i -> System.out.println("判斷是否為Intger對象,i:" + i);
case Student s -> System.out.println("判斷是否為具體學(xué)生對象,student:" + s.toString());
case UserCommonService -> System.out.println("判斷是否為普通用戶實現(xiàn)類,然后走普通用戶邏輯");
case UserVipService -> System.out.println("判斷是否為vip用戶實現(xiàn)類,然后走vip用戶邏輯");
default -> System.out.println("Something else");
}
- 默認啟用 Parallel GC。
- 增強TreeMap。
- 統(tǒng)一日志異步刷新,先將日志寫入緩存,獨立線程負責(zé)刷新到相應(yīng)輸出。
Java 18
- Java API 標準庫中的字符編碼默認為 UTF-8,也就是說我們寫代碼在處理字符時,不再需要顯示指定 UTF-8 編碼。
- 提供了一個命令行工具來啟動建議的 Web Server,它是一個文件服務(wù)。
- 支持在 Java API 文檔中加入代碼片段,如下面代碼:
/**
* The following code shows how to use {@code Optional.isPresent}:
* {@snippet :
* if (v.isPresent()) {
* System.out.println("v: " + v.get());
* }
* }
*/
- 基于方法句柄重新實現(xiàn)的 java.lang.reflect 作為平臺通用的反射底層實現(xiàn)機制,以替代基于字節(jié)碼生成機制的 Method::invoke, Constructor::newInstance, Field::get 和 Field::set。
- 引入向量 API,如下面代碼:
//不使用向量 API 的寫法:
void scalarComputation(float[] a, float[] b, float[] c) {
for (int i = 0; i < a.length; i++) {
c[i] = (a[i] * a[i] + b[i] * b[i]) * -1.0f;
}
}
//使用向量 API 寫法:
void vectorComputation(float[] a, float[] b, float[] c) {
for (int i = 0; i < a.length; i += SPECIES.length()) {
// VectorMask<Float> m;
var m = SPECIES.indexInRange(i, a.length);
// FloatVector va, vb, vc;
var va = FloatVector.fromArray(SPECIES, a, i, m);
var vb = FloatVector.fromArray(SPECIES, b, i, m);
var vc = va.mul(va)
.add(vb.mul(vb))
.neg();
vc.intoArray(c, i, m);
}
}
- 引入互聯(lián)網(wǎng)地址解析 SPI。
- 外部函數(shù)和內(nèi)存 API,引入目的是提升易用性、性能、通用性和安全性,詳見 JEP 419。
- 模式匹配 Switch 表達式。
- 棄用 Finalization。
Java 19
Java 19 引入的主要是預(yù)覽和孵化的新特性,包括:Record模式、將 JDK 移植到 Linux/RISC-V、外部函數(shù)和內(nèi)存API、虛擬線程、向量API、模式匹配的 Switch、使用結(jié)構(gòu)化并發(fā)方式實現(xiàn)并發(fā)編程。
下面主要看看一下向量 API。實例代碼如下:
//不使用向量 API 的寫法:
void scalarComputation(float[] a, float[] b, float[] c) {
for (int i = 0; i < a.length; i++) {
c[i] = (a[i] * a[i] + b[i] * b[i]) * -1.0f;
}
}
//使用向量 API 寫法:
static final VectorSpecies<Float> SPECIES = FloatVector.SPECIES_PREFERRED;
void vectorComputation(float[] a, float[] b, float[] c) {
for (int i = 0; i < a.length; i += SPECIES.length()) {
// VectorMask<Float> m;
var m = SPECIES.indexInRange(i, a.length);
// FloatVector va, vb, vc;
var va = FloatVector.fromArray(SPECIES, a, i, m);
var vb = FloatVector.fromArray(SPECIES, b, i, m);
var vc = va.mul(va)
.add(vb.mul(vb))
.neg();
vc.intoArray(c, i, m);
}
}
Java 20
Java 20 引入的都是孵化和預(yù)覽功能,下面簡單看一下:
- 作用域值。
- Record 模式。
- 模式匹配的 Switch 表達式。
- 外部函數(shù)與內(nèi)存 API。
- 虛擬線程。
- 結(jié)構(gòu)化并發(fā)。
- 向量 API。
Java 21
Java 21 是一個 LTS 版本,新增特性比較多,其中預(yù)覽和孵化特性包括:
- 結(jié)構(gòu)化并發(fā)。
- 向量 API。
- 作用域值。
- 未命名類和 main 方法。
- 未命名模式和變量。
- 外部函數(shù)和內(nèi)存 API。
- switch 模式匹配。
正式特性包括:
- 密鑰封裝機制 API,通過公鑰加密來保護對稱密鑰。
- 準備禁用動態(tài)加載代理。在這個版本中,如果使用動態(tài)加載代理,會出現(xiàn)下面警告:
WARNING: A {Java,JVM TI} agent has been loaded dynamically (file:/u/bob/agent.jar)
WARNING: If a serviceability tool is in use, please run with -XX:+EnableDynamicAgentLoading to hide this warning
WARNING: If a serviceability tool is not in use, please run with -Djdk.instrument.traceUsage for more information
WARNING: Dynamic loading of agents will be disallowed by default in a future release
- 棄用 Windows 32 位 x86 端口。
- 虛擬線程,虛擬線程是輕量級線程,可以減少編寫、維護和觀察高并發(fā)應(yīng)用的工作量。下面是一個官方示例代碼,首先創(chuàng)建一個 ExecutorService,它將為每個提交的任務(wù)創(chuàng)建一個虛擬線程。它將創(chuàng)建 10000 個虛擬線程并且等待它們執(zhí)行完成。
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
IntStream.range(0, 10_000).forEach(i -> {
executor.submit(() -> {
Thread.sleep(Duration.ofSeconds(1));
return i;
});
});
} // executor.close() is called implicitly, and waits
- switch 模式匹配??聪旅婺J狡ヅ涞拇a:
//Java 21 以前
static String formatter(Object obj) {
String formatted = "unknown";
if (obj instanceof Integer i) {
formatted = String.format("int %d", i);
} else if (obj instanceof Long l) {
formatted = String.format("long %d", l);
} else if (obj instanceof Double d) {
formatted = String.format("double %f", d);
} else if (obj instanceof String s) {
formatted = String.format("String %s", s);
}
return formatted;
}
//Java 21 以后
static String formatterPatternSwitch(Object obj) {
return switch (obj) {
case Integer i -> String.format("int %d", i);
case Long l -> String.format("long %d", l);
case Double d -> String.format("double %f", d);
case String s -> String.format("String %s", s);
default -> obj.toString();
};
}
再看一下對 null 判斷的簡化:
// Prior to Java 21
static void testFooBarOld(String s) {
if (s == null) {
System.out.println("Oops!");
return;
}
switch (s) {
case "Foo", "Bar" -> System.out.println("Great");
default -> System.out.println("Ok");
}
}
// As of Java 21
static void testFooBarNew(String s) {
switch (s) {
case null -> System.out.println("Oops");
case "Foo", "Bar" -> System.out.println("Great");
default -> System.out.println("Ok");
}
}
除此之外,switch 模式匹配還有很多內(nèi)容,具體可以看下面的鏈接:
https://openjdk.org/jeps/441
- Record Patterns,它允許我們在模式匹配中使用 record types。record types 是一種新的類聲明形式,在 Java 16 中引入,用于定義不可變的數(shù)據(jù)對象。Record Patterns 提供了簡單的方式來進行模式匹配,并且可以方便地從 record types 中提取參數(shù)值??聪旅婀俜绞纠a:
// As of Java 16
record Point(int x, int y) {}
static void printSum(Object obj) {
if (obj instanceof Point p) {
int x = p.x();
int y = p.y();
System.out.println(x+y);
}
}
// As of Java 21
static void printSum(Object obj) {
if (obj instanceof Point(int x, int y)) {
System.out.println(x+y);
}
}
更多精彩的示例見下面鏈接:
https://openjdk.org/jeps/440
- 引入 Generational ZGC,旨在減少處理大型堆內(nèi)存時可能會導(dǎo)致長時間的停頓。
Java 22
Java 22 引入孵化和預(yù)覽的特性如下:
- 作用域值。
- super(...)前導(dǎo)語句,允許在調(diào)用 super 方法之前引入其他語句,比如參數(shù)校驗。見下面代碼:
//之前的寫法
public class PositiveBigInteger extends BigInteger {
public PositiveBigInteger(long value) {
super(value); // Potentially unnecessary work
if (value <= 0)
throw new IllegalArgumentException("non-positive value");
}
}
//使用前導(dǎo)語句的寫法
public class PositiveBigInteger extends BigInteger {
public PositiveBigInteger(long value) {
if (value <= 0)
throw new IllegalArgumentException("non-positive value");
super(value);
}
}
- 類文件 API。
- String 模版。
- 向量 API。
- 流聚合器,引入 gather 操作,用戶可以自定義中間操作,比如下面的 a、b、c
source.gather(a).gather(b).gather(c).collect(...)
- 結(jié)構(gòu)化并發(fā)。
- 隱式聲明類和實例主方法。static 修飾符、String[] 參數(shù)只有需要的時候再加。
class HelloWorld {
void main() {
System.out.println("Hello, World!");
}
}
Java 22 引入的正式特性包括:
- 啟動多文件源碼程序,比如一個目錄下有下面的文件:
Prog1.java
Prog2.java
Helper.java
library1.jar
library2.jar
下面命令('*')可以把在目錄下面的 jar 包都放到 classpath,以方便地運行 Java 程序。
java --class-path '*' Prog1.java
- 未命名變量和模式,讓代碼更簡潔??聪旅娲a示例:
//使用未命名變量之前
Queue<Integer> q = ... // x1, y1, z1, x2, y2, z2 ..
while (q.size() >= 3) {
int x = q.remove();
int y = q.remove();
int z = q.remove(); // z is unused
... new Point(x, y) ...
}
//使用未命名變量之后
while (q.size() >= 3) {
var x = q.remove();
var _ = q.remove(); // Unnamed variable
var _ = q.remove(); // Unnamed variable
... new Point(x, 0) ...
}
- 外部函數(shù)和內(nèi)存 API,可以方便地調(diào)用外部函數(shù)和管理內(nèi)存。
Java 23
Java 23 引入孵化和預(yù)覽的特性如下:
- Patterns、instanceof 和 switch 可以使用所有基礎(chǔ)類型。
- 類文件 API。
- 向量 API。
- 流聚合器。
- Module Import 聲明:
import module java.sql
- 隱式聲明類和實例主方法。
- 結(jié)構(gòu)化并發(fā)。
- 作用域值。
- 靈活構(gòu)造函數(shù)體。下面是示例代碼:
//下面是之前的寫法
public class PositiveBigInteger extends BigInteger {
public PositiveBigInteger(long value) {
super(value); // Potentially unnecessary work
if (value <= 0) throw new IllegalArgumentException(..);
}
}
//使用靈活構(gòu)造函數(shù)體后,寫法變成
public class PositiveBigInteger extends BigInteger {
public PositiveBigInteger(long value) {
if (value <= 0) throw new IllegalArgumentException(..);
super(value);
}
}
Java 23 引入的正式特性包括:
- 棄用 sun.misc.Unsafe 中的內(nèi)存訪問方法。
- ZGC 默認使用分代模式。
- 文檔注釋可以使用 Markdown,看下面的官方示例,很香:
/// Returns a hash code value for the object. This method is
/// supported for the benefit of hash tables such as those provided by
/// [java.util.HashMap].
///
/// The general contract of `hashCode` is:
///
/// - Whenever it is invoked on the same object more than once during
/// an execution of a Java application, the `hashCode` method
/// must consistently return the same integer, provided no information
/// used in `equals` comparisons on the object is modified.
/// This integer need not remain consistent from one execution of an
/// application to another execution of the same application.
/// - If two objects are equal according to the
/// [equals][#equals(Object)] method, then calling the
/// `hashCode` method on each of the two objects must produce the
/// same integer result.
/// - It is _not_ required that if two objects are unequal
/// according to the [equals][#equals(Object)] method, then
/// calling the `hashCode` method on each of the two objects
/// must produce distinct integer results. However, the programmer
/// should be aware that producing distinct integer results for
/// unequal objects may improve the performance of hash tables.
///
/// @implSpec
/// As far as is reasonably practical, the `hashCode` method defined
/// by class `Object` returns distinct integers for distinct objects.
///
/// @return a hash code value for this object.
/// @see java.lang.Object#equals(java.lang.Object)
/// @see java.lang.System#identityHashCode
寫在最后
其實每個 Java 版本發(fā)布的新特性并不多,而且好多特性要進行多個版本的孵化和預(yù)覽。雖然公司的 Java 版本肯定跟不上 Java 版本發(fā)布的節(jié)奏,但作為程序員的我們,可以關(guān)注下 Java 的新增特性。