JDK14新特性介紹
描述
OpenJDK14是Java SE平臺(tái)的版本14的開(kāi)源參考實(shí)現(xiàn),由Java社區(qū)流程中的JSR 389指定 。2020年3月17日J(rèn)DK14達(dá)到一般可用性, 基于GPL協(xié)議的構(gòu)建許可文件可從Oracle獲得; 其他供應(yīng)商的二進(jìn)制文件將在不久之后。已通過(guò)JEP 2.0提案修訂的JEP流程提出并跟蹤了此版本的功能和時(shí)間表曹动。該發(fā)行版是使用JDK發(fā)行流程(JEP 3)制作的。
此次的更新包含了很多令人興奮的新功能∈樵冢總共有非常令人印象深刻的16個(gè)JDK增強(qiáng)建議(JEP)和69個(gè)新的API元素。
新功能
Records
Java是一種面向?qū)ο蟮恼Z(yǔ)言拆又。您可以創(chuàng)建類來(lái)保存數(shù)據(jù)儒旬,并使用封裝來(lái)控制如何訪問(wèn)和修改該數(shù)據(jù)。對(duì)象的使用使操作復(fù)雜的數(shù)據(jù)類型變得簡(jiǎn)單而直接帖族。這是Java如此流行作為平臺(tái)的原因之一栈源。缺點(diǎn)(到現(xiàn)在為止)是,創(chuàng)建數(shù)據(jù)類型非常冗長(zhǎng)竖般,即使在最直接的情況下也需要大量代碼甚垦。讓我們看一下基本二維點(diǎn)所需的代碼:
public class Point {
private final double x;
private final double y;
public Point(double x, double y) {
this.x = x;
this.y = y;
}
public double getX() {
return x;
}
public double getY() {
return y;
}
}
這14行代碼僅僅表示了2個(gè)值元。
JDK 14引入了記錄(records)作為預(yù)覽功能涣雕。預(yù)覽功能是一個(gè)新概念艰亮,使Java平臺(tái)的開(kāi)發(fā)人員可以在不使其成為Java SE標(biāo)準(zhǔn)一部分的情況下包括新的語(yǔ)言功能。通過(guò)這樣做挣郭,開(kāi)發(fā)人員可以嘗試這些功能迄埃,并提供反饋,以便在必要時(shí)在標(biāo)準(zhǔn)中設(shè)置功能之前進(jìn)行更改(甚至刪除功能)兑障。要使用預(yù)覽功能侄非,必須為編譯和運(yùn)行時(shí)指定命令行標(biāo)志--enable-preview
。對(duì)于編譯旺垒,還必須指定-source
標(biāo)志彩库。
記錄是表示數(shù)據(jù)類的一種簡(jiǎn)單得多的方法。如果以Point為例先蒋,代碼可以簡(jiǎn)化為一行:
public record Point(double x, double y) { }
這與代碼的可讀性無(wú)關(guān)骇钦。我們立即意識(shí)到,我們現(xiàn)在有了一個(gè)包含兩個(gè)稱為x和y的雙精度值的類竞漾,我們可以使用getX和getY的標(biāo)準(zhǔn)方法名稱進(jìn)行訪問(wèn)眯搭。
讓我們檢查一下記錄的一些細(xì)節(jié)窥翩。
首先,記錄是一種新的類型鳞仙,它是類的受限形式寇蚊,就像枚舉一樣。記錄具有名稱和狀態(tài)描述棍好,用于定義記錄的組成部分仗岸。在上面的Point示例中,狀態(tài)描述是double和x和y借笙。記錄是為了簡(jiǎn)化而設(shè)計(jì)的扒怖,因此它們不能擴(kuò)展任何其他類或定義其他實(shí)例變量。記錄中的所有狀態(tài)都是最終狀態(tài)业稼,因此不提供set方法盗痒。如果您需要任何一個(gè),則需要使用成熟的類低散。
記錄確實(shí)具有靈活性俯邓。
通常,構(gòu)造函數(shù)除了提供值外還需要提供其他行為熔号。如果是這種情況稽鞭,我們可以提供構(gòu)造函數(shù)的替代實(shí)現(xiàn):
record Range(int min, int max) {
Range {
if (min > max)
throw new IllegalArgumentException(“Max must be >= min”);
}
}
請(qǐng)注意,由于指定參數(shù)是多余的跨嘉,因此構(gòu)造方法的定義仍被縮寫(xiě)川慌。也可以聲明從狀態(tài)描述自動(dòng)派生的任何成員,因此祠乃,例如梦重,您可以提供toString ()或hashCode ()實(shí)現(xiàn)的替代方法。
instanceof
在某些情況下亮瓷,您不知道對(duì)象的確切類型琴拧。為了解決這個(gè)問(wèn)題,Java有instanceof運(yùn)算符嘱支,可用于針對(duì)不同類型進(jìn)行測(cè)試蚓胸。這樣做的缺點(diǎn)是確定了對(duì)象的類型。如果要使用顯式類型轉(zhuǎn)換除师,則必須使用顯式類型轉(zhuǎn)換:
if (o instanceof String) {
String s = (String)o;
System.out.println(s.length);
}
在JDK 14中沛膳,對(duì)instanceof運(yùn)算符進(jìn)行了擴(kuò)展,以允許除了類型之外還指定變量名汛聚。然后可以使用該變量而無(wú)需顯式強(qiáng)制轉(zhuǎn)換:
if (o instanceof String s)
System.out.println(s.length);
變量的范圍限于在邏輯上正確使用的地方锹安,因此:
if (o instanceof String s)
System.out.println(s.length);
else
// s在這里超出范圍
該范圍也可以在條件語(yǔ)句中應(yīng)用,因此我們可以執(zhí)行以下操作:
if (o instanceof String s && s.length() > 4) ...
這是有意義的,因?yàn)閘ength()方法將只是被稱為如果 o為一個(gè)字符串叹哭。邏輯或運(yùn)算符不一樣:
if (o instanceof String s || s.length() > 4) ...
在這種情況下忍宋,無(wú)論o是否為String的結(jié)果,都需要對(duì)s.length()進(jìn)行求值风罩。從邏輯上講糠排,這不起作用,因此會(huì)導(dǎo)致編譯錯(cuò)誤超升。
使用邏輯非運(yùn)算符可以產(chǎn)生一些有趣的作用域效果:
if (!(o instanceof String s && s.length() > 3)
return;
System.out.println(s.length()); // s 在這里
我已經(jīng)看到了對(duì)變量的范圍劃分方式的一些負(fù)面反饋入宦,但是,鑒于所有作用域都是完全合乎邏輯的廓俭,因此我認(rèn)為它非常有效云石。
已經(jīng)有計(jì)劃提供此功能的第二個(gè)預(yù)覽版本,該版本將擴(kuò)展模式匹配以與記錄一起使用研乒,并提供實(shí)現(xiàn)解構(gòu)模式的簡(jiǎn)單方法×芟酰可以在JEP 375中找到更多詳細(xì)信息雹熬。
有幫助的NullPointException
任何編寫(xiě)了多行Java代碼的人都將在某個(gè)時(shí)候遇到NullPointerException。未能初始化對(duì)象引用(或?qū)⑵溴e(cuò)誤地顯式設(shè)置為null)谣膳,然后嘗試使用該引用將導(dǎo)致引發(fā)此異常竿报。
在簡(jiǎn)單的情況下,找出問(wèn)題的原因很簡(jiǎn)單继谚。如果我們嘗試運(yùn)行以下代碼:
public class NullTest {
List<String> list;
public NullTest() {
list.add("foo");
}
}
生成的錯(cuò)誤是:
Exception in thread "main" java.lang.NullPointerException
at jdk14.NullTest.<init>(NullTest.java:16)
at jdk14.Main.main(Main.java:15)
由于我們?cè)诘?6行上引用列表烈菌,因此很明顯,列表是罪魁禍?zhǔn)谆模覀兛梢钥焖俳鉀Q問(wèn)題芽世。
但是,如果我們?cè)谶@樣的行中使用鏈接引用:
a.b.c.d = 12;
運(yùn)行此命令時(shí)诡壁,我們可能會(huì)看到如下錯(cuò)誤:
Exception in thread "main" java.lang.NullPointerException
at Prog.main(Prog.java:5)
問(wèn)題在于我們無(wú)法由此確定異常是由于a為null济瓢,b為null還是c為null的結(jié)果。我們要么需要使用IDE中的調(diào)試器妹卿,要么更改代碼旺矾,將引用分隔到不同的行上。兩者都不是理想的夺克。
在JDK 14中箕宙,如果我們運(yùn)行相同的代碼,我們將看到類似以下內(nèi)容:
Exception in thread "main" java.lang.NullPointerException:
Cannot read field "c" because "a.b" is null
at Prog.main(Prog.java:5)
馬上铺纽,我們可以看到ab是問(wèn)題所在柬帕,并著手進(jìn)行糾正。我敢肯定,這將使許多Java開(kāi)發(fā)人員的生活更加輕松雕崩,絕對(duì)拍手叫好的魁索,更好的定位才是解決bug的最佳途徑。
新API
java.io
PrintStream有兩個(gè)新方法盼铁,write(byte [] buf)和writeBytes(byte [] buf)粗蔚。這些有效地做同樣的事情,等效于write(buf饶火,0鹏控,buf.length)。之所以擁有兩種不同的方法肤寝,是因?yàn)閷rite定義為拋出IOException(但是当辐,奇怪的是,從不拋出)鲤看,而writeBytes沒(méi)有缘揪。因此,選擇使用哪種方法取決于您是否希望使用try-catch塊包圍該調(diào)用义桂。
有一個(gè)新的注釋類型找筝,Serial
。它旨在用于對(duì)序列化的編譯器檢查慷吊。特別是袖裕,此類型的注釋?xiě)?yīng)應(yīng)用于與序列化相關(guān)的方法和聲明為可序列化的類中的字段。(它在某些方面與Override注釋類似)溉瓶。
java.lang
Class
類為新的Record
功能提供了兩種方法:isRecord()
和getRecordComponents()
急鳄。getRecordComponents()
方法返回一個(gè)RecordComponent
對(duì)象數(shù)組。RecordComponent
是java.lang.reflect
包中的新類堰酿,它具有11種方法來(lái)檢索內(nèi)容疾宏,例如注釋的詳細(xì)信息和泛型。
Record
是一個(gè)簡(jiǎn)單的新類胞锰,它重寫(xiě)Object的equals灾锯,hashCode和toString方法。
NullPointerException
現(xiàn)在作為有幫助的NullPointerExceptions功能的一部分覆蓋了Throwable中的getMessage方法嗅榕。
StrictMath
類具有六個(gè)新方法顺饮,這些方法補(bǔ)充了需要檢測(cè)溢出錯(cuò)誤時(shí)使用的現(xiàn)有精確方法。新方法是decrementExact凌那,incrementExact和negateExact(所有方法都有int和long參數(shù)的兩個(gè)重載版本)兼雄。
java.lang.annotation
ElementType
枚舉為Records
添加了一個(gè)新的常量RECORD_TYPE。
java.lang.invoke
MethodHandles.Lookup類具有兩個(gè)新方法:
- hasFullPrivilegeAccess帽蝶,如果要查找的方法同時(shí)具有PRIVATE和MODULE訪問(wèn)權(quán)赦肋,則返回true。
- previousLookupClass,它報(bào)告此查找對(duì)象先前從其傳送過(guò)來(lái)的另一個(gè)模塊中的查找類佃乘,或者為null囱井。以前,我從未聽(tīng)說(shuō)過(guò)在Java上下文中進(jìn)行隱形傳送(僅在Doctor Who和Minecraft中)趣避。查找可能導(dǎo)致模塊之間的傳送庞呕。
java.lang.runtime
這是JDK 14中的新軟件包,只有一個(gè)類ObjectMethods程帕。這是記錄功能的低級(jí)部分住练,它具有單個(gè)方法(引導(dǎo)程序),該方法生成對(duì)象等于愁拭,hashCode和toString方法讲逛。
java.util.text
CompactNumberFormat類具有一個(gè)新的構(gòu)造函數(shù),該構(gòu)造函數(shù)為pluralRules添加了一個(gè)參數(shù)岭埠。這是一個(gè)String盏混,表示將Count關(guān)鍵字(例如“ one”)與實(shí)際整數(shù)關(guān)聯(lián)的多個(gè)規(guī)則。它的語(yǔ)法在Unicode Consortium的復(fù)數(shù)規(guī)則語(yǔ)法中定義枫攀。
java.util
HashSet類具有一個(gè)新方法toArray括饶,該方法返回一個(gè)數(shù)組,其運(yùn)行時(shí)組件類型為Object来涨,包含此集合中的所有元素。
java.util.concurrent.locks
LockSupport
類具有一個(gè)新方法setCurrentBlocker启盛。LockSupport
提供了暫存和取消暫存線程的功能(與不贊成使用的Thread.suspend和Thread.resume方法沒(méi)有相同的問(wèn)題)”钠現(xiàn)在可以設(shè)置getBlocker將返回的Object。從非公共對(duì)象調(diào)用無(wú)參數(shù)方法時(shí)僵闯,這很有用卧抗。
javax.lang.model.element
ElementKind
枚舉具有三個(gè)新常量,用于記錄和模式匹配實(shí)例的特征鳖粟,即BINDING_VARIABLE
社裆,RECORD
和RECORD_COMPONENT
。
javax.lang.model.util
該軟件包提供實(shí)用程序向图,以協(xié)助處理程序元素和類型泳秀。通過(guò)添加記錄,添加了一組新的抽象類和具體類來(lái)支持此功能榄攀。(示例是AbstractTypeVisitor14嗜傅,ElementScanner14和TypeKindVisitor14)。
org.xml.sax
一種新方法已添加到SAX XML解析器的ContentHandler接口檩赢。聲明方法接收XML聲明的通知吕嘀。在默認(rèn)實(shí)現(xiàn)的情況下,此操作無(wú)效。
JEP 370:外部?jī)?nèi)存訪問(wèn)API
這是作為孵化器模塊引入的偶房,以允許更廣泛的Java社區(qū)進(jìn)行測(cè)試趁曼,并在其成為Java SE標(biāo)準(zhǔn)的一部分之前集成反饋。它旨在替代sun.misc.Unsafe
和java.io.MappedByteBuffer
棕洋。
外部存儲(chǔ)器訪問(wèn)API引入了三個(gè)主要抽象:
- MemorySegment:提供對(duì)具有給定范圍的連續(xù)內(nèi)存區(qū)域的訪問(wèn)挡闰。
- MemoryAddress:提供到MemorySegment的偏移量(基本上是一個(gè)指針)。
- MemoryLayout:提供一種描述內(nèi)存段布局的方法拍冠,該方法大大簡(jiǎn)化了使用var句柄訪問(wèn)MemorySegment的過(guò)程尿这。使用此功能,無(wú)需根據(jù)內(nèi)存的使用方式來(lái)計(jì)算偏移量庆杜。例如射众,一個(gè)整數(shù)或長(zhǎng)整數(shù)數(shù)組的偏移量將有所不同,但將使用MemoryLayout透明地對(duì)其進(jìn)行處理晃财。
JVM改變
JEP 345: G1的NUMA感知內(nèi)存分配叨橱。這樣可以提高使用非均勻內(nèi)存體系結(jié)構(gòu)(NUMA)的大型計(jì)算機(jī)的性能。
JEP 363: 刪除并發(fā)標(biāo)記掃描(CMS)垃圾收集器断盛。從JDK 9開(kāi)始罗洗,G1已經(jīng)成為默認(rèn)的收集器,大多數(shù)(但不是全部)都認(rèn)為G1是CMS的高級(jí)收集器钢猛』锊耍考慮到維護(hù)兩個(gè)相似的配置文件收集器所需的資源,Oracle決定棄用CMS(在JDK 9中也是如此)命迈,現(xiàn)在將其刪除贩绕。如果您正在尋找一款高性能,低延遲替代壶愤,為什么不嘗試 Zing JVM的C4淑倾?
JEP 349: Java Flight Recorder事件流。通過(guò)啟用工具以異步方式訂閱Java Flight Recorder事件征椒,這可以對(duì)JVM進(jìn)行更實(shí)時(shí)的監(jiān)視娇哆。
JEP 364: macOS上的ZGC和 JEP 365:Windows上的ZGC。ZGC是實(shí)驗(yàn)性的低延遲收集器勃救,最初僅在Linux上受支持“郑現(xiàn)在,它已擴(kuò)展到macOS和Windows操作系統(tǒng)剪芥。
JEP 366: 棄用ParallelScavenge和SerialOld GC組合垄开。Oracle指出,很少有人會(huì)使用這種組合税肪,并且維護(hù)開(kāi)銷(xiāo)相當(dāng)大溉躲。期望在不久的將來(lái)某個(gè)時(shí)候可以刪除此組合榜田。
其它功能
有許多與OpenJDK的不同部分相關(guān)的JEP:
JEP 343:打包工具。這是一個(gè)簡(jiǎn)單的打包工具锻梳,基于從JDK 11中的Oracle JDK中刪除的JavaFX javapackager工具箭券。它有很多功能,因此我將在單獨(dú)的博客文章中詳細(xì)介紹疑枯。
JEP 352:非易失性映射字節(jié)緩沖區(qū)辩块。這添加了新的特定于JDK的文件映射模式,以便可以使用FileChannel API創(chuàng)建引用非易失性存儲(chǔ)器的MappedByteBuffer實(shí)例荆永。添加了一個(gè)新模塊jdk.nio.mapmode废亭,以允許將MapMode設(shè)置為READ_ONLY_SYNC或WRITE_ONLY_SYNC。
JEP 361:開(kāi)關(guān)表達(dá)式標(biāo)準(zhǔn)具钥。開(kāi)關(guān)表達(dá)式是JDK 12中添加到OpenJDK的第一個(gè)預(yù)覽功能豆村。在JDK 13中,反饋導(dǎo)致將break值語(yǔ)法更改為yield 值骂删。 在JDK 14中掌动,開(kāi)關(guān)表達(dá)式不再是預(yù)覽功能,并且已包含在Java SE標(biāo)準(zhǔn)中宁玫。
JEP 362:棄用Solaris和SPARC端口粗恢。由于Oracle不再開(kāi)發(fā)Solaris操作系統(tǒng)或SPARC芯片體系結(jié)構(gòu),因此他們不再需要繼續(xù)維護(hù)這些端口欧瘪。這些可能會(huì)被Java社區(qū)中的其他人所接受眷射。
JEP 367:刪除Pack 200工具和API。JDK 11中不推薦使用的另一個(gè)功能現(xiàn)在已從JDK 14中刪除佛掖。此壓縮格式的主要用途是用于applet使用的jar文件凭迹。鑒于瀏覽器插件已從Oracle JDK 11中刪除,這似乎是一個(gè)合理的功能苦囱。
JEP 368:文本塊。這些已作為預(yù)覽功能包含在JDK 13中脾猛,并且在JDK 14中繼續(xù)進(jìn)行第二次迭代(仍在預(yù)覽中)撕彤。文本塊提供了對(duì)多行字符串文字的支持。所做的更改是添加了兩個(gè)新的轉(zhuǎn)義序列猛拴。第一個(gè)通過(guò)在行的末尾添加\來(lái)抑制換行符的包含(這在軟件開(kāi)發(fā)的許多其他地方很常見(jiàn))羹铅。第二個(gè)是\ s,表示單個(gè)空格愉昆。這對(duì)于防止從文本塊中的行尾剝離空白很有用职员。