JDK15真的來了,一起來看看它的新特性

{"type":"doc","content":[{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"簡介"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一年兩次的JDK最新版本JDK15在2020年9月15日正式發佈了,這次的JDK15給我們帶了隱藏類,EdDSA,模式匹配,Records,封閉類和Text Block等諸多新特性。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一起來看看吧。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"JDK15的新特性"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"JEP 385 Deprecate RMI Activation for Removal"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"RMI Activation被標記爲Deprecate,將會在未來的版本中刪除。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"RMI大家應該都清楚,RMI就是Remote Method Invocation,翻譯成中文就是遠程方法調用,是在JDK1.2中引入的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"RMI爲java提供了開發分佈式系統的強大能力。而J2EE的規範EJB就是使用RMI來實現的bean的遠程調用的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在RMI系統中,遠程系統中存在很多分佈式對象,如果這些分佈式對象一直處於活動狀態的話,將會佔用很多寶貴的系統資源。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"於是RMI引入了一種lazy Activation的方式,這種方式就叫做延遲激活。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這裏有兩個概念,活動對象和被動對象。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"活動對象是在某些系統上的JVM中實例化並對外暴露的遠程對象。被動對象是尚未在JVM中實例化(或暴露)但可以進入主動狀態的對象。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"將被動對象轉換爲主動對象的過程稱爲激活。激活要求對象與JVM關聯,這可能會將該對象的類加載到JVM中,並且將該對象恢復爲之前的狀態。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在RMI系統中,我們使用延遲激活。延遲激活將激活對象推遲到客戶第一次使用(即第一次方法調用)之前。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"既然RMI Activation這麼好用,爲什麼要廢棄呢?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"因爲對於現代應用程序來說,分佈式系統大部分都是基於Web的,web服務器已經解決了穿越防火牆,過濾請求,身份驗證和安全性的問題,並且也提供了很多延遲加載的技術。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"所以在現代應用程序中,RMI Activation已經很少被使用到了。並且在各種開源的代碼庫中,也基本上找不到RMI Activation的使用代碼了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"爲了減少RMI Activation的維護成本,在JDK8中,RMI Activation被置爲可選的。現在在JDK15中,終於可以廢棄了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"JEP 371 Hidden Classes"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Hidden Classes是什麼呢?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Hidden Classes就是不能直接被其他class的二淨值代碼使用的class。Hidden Classes主要被一些框架用來生成運行時類,但是這些類不是被用來直接使用的,而是通過反射機制來調用。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通常來說基於JVM的很多語言都有動態生成類的機制,這樣可以提高語言的靈活性和效率。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"比如在JDK8中引入的lambda表達式,JVM並不會在編譯的時候將lambda表達式轉換成爲專門的類,而是在運行時將相應的字節碼動態生成相應的類對象。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"另外使用動態代理也可以爲某些類生成新的動態類。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"那麼我們希望這些動態生成的類需要具有什麼特性呢?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":null,"normalizeStart":1},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"不可發現性。因爲我們是爲某些靜態的類動態生成的動態類,所以我們希望把這個動態生成的類看做是靜態類的一部分。所以我們不希望除了該靜態類之外的其他機制發現。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"訪問控制。我們希望在訪問控制靜態類的同時,也能控制到動態生成的類。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"生命週期。動態生成類的生命週期一般都比較短,我們並不需要將其保存和靜態類的生命週期一致。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"但是現有的類的定義API ClassLoader::defineClass和Lookup::defineClass是不管類的字節碼是如何生成的,他們都是平等對待。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"所以我們需要一些API來定義無法發現的且具有有限生命週期的隱藏類。這將提高所有基於JVM的語言實現的效率。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"比如:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"java.lang.reflect.Proxy可以定義隱藏類作爲實現代理接口的代理類。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"java.lang.invoke.StringConcatFactory可以生成隱藏類來保存常量連接方法;"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"java.lang.invoke.LambdaMetaFactory可以生成隱藏的nestmate類,以容納訪問封閉變量的lambda主體;"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"JavaScript引擎可以爲從JavaScript程序轉換的字節碼生成隱藏的類,因爲當引擎不再使用它們時,這些類將被卸載。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"普通類是通過調用ClassLoader::defineClass創建的,而隱藏類是通過調用Lookup::defineHiddenClass創建的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這使JVM從提供的字節中派生一個隱藏類,鏈接該隱藏類,並返回提供對隱藏類的反射訪問的查找對象。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"調用程序可以通過返回的查找對象來獲取隱藏類的Class對象。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"JEP 339 Edwards-Curve Digital Signature Algorithm (EdDSA)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"實現了EdDSA橢圓曲線簽名算法。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這裏就不多講橢圓曲線簽名算法了,如果又想了解的朋友可以給我留言。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"JEP 375 Pattern Matching for instanceof (Second Preview)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Pattern Matching 就是說可以在做pattern mathching的時候,直接對該對象進行類型的轉換。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"現在這個特性還是預覽版本的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們看一下具體的例子:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"if (obj instanceof String) {\n String s = (String) obj;\n // use s\n}\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在Pattern Matching之前,我們使用instanceof之後,還需要對該對象進行強制類型轉換才能使用。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"但是在Pattern Matching之後,我們可以這樣用:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"if (obj instanceof String s) {\n // can use s here\n} else {\n // can't use s here\n}\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"是不是很方便。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"JEP 384 Records (Second Preview)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Record是一種輕量級的class,可以看做是數據結構體。和scala中的case有點相似。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"舉個自定義User的例子看一下Record是怎麼用的:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"public record Address(\n String addressName,\n String city\n) {\n}\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"public record CustUser(\n String firstName,\n String lastName,\n Address address,\n int age\n) {}\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上面我們定義了兩個類,CustUser和Address。CustUser中引用了Address。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Record和普通的類的區別就在於Record多了一個括號括起來的定義的字段。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Record類默認是final的,裏面的字段默認是private final的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"要想知道Record到底是怎麼工作的,我們可以使用javap來對編譯好的class文件反編譯,運行javap CustUser,可以得到下面的結果:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"警告: 二進制文件CustUser包含com.flydean.records.CustUser\nCompiled from \"CustUser.java\"\npublic final class com.flydean.records.CustUser extends java.lang.Record {\n public com.flydean.records.CustUser(java.lang.String, java.lang.String, com.flydean.records.Address, int);\n public java.lang.String toString();\n public final int hashCode();\n public final boolean equals(java.lang.Object);\n public java.lang.String firstName();\n public java.lang.String lastName();\n public com.flydean.records.Address address();\n public int age();\n}\n\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上面可以看到final class CustUser繼承自java.lang.Record。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"並且自動添加了默認帶有所有字段的構造函數。各個自動的獲取方法,並實現了toString,hashCode和equals方法。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"天啦,太完美了,我們想要的它居然都有。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果上面的javap還不是很清楚的話,大家可以藉助IDE的反編譯功能,打開CustUser.class文件看一看:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"public final class CustUser extends java.lang.Record {\n private final java.lang.String firstName;\n private final java.lang.String lastName;\n private final com.flydean.records.Address address;\n private final int age;\n\n public CustUser(java.lang.String firstName, java.lang.String lastName, com.flydean.records.Address address, int age) { /* compiled code */ }\n\n public java.lang.String toString() { /* compiled code */ }\n\n public final int hashCode() { /* compiled code */ }\n\n public final boolean equals(java.lang.Object o) { /* compiled code */ }\n\n public java.lang.String firstName() { /* compiled code */ }\n\n public java.lang.String lastName() { /* compiled code */ }\n\n public com.flydean.records.Address address() { /* compiled code */ }\n\n public int age() { /* compiled code */ }\n}\n"}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"italic"}],"text":"注意,上面的反編譯我們可以看到,record中的所有字段都是final的,只能在初始化的時候設置。並且方法裏面也沒有提供其他可以改變字段內容的方法。"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"所以我們得出了一個震世驚俗的結論:record是immutable的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上面的例子中我們只使用了小括號裏面的內容,大括號還是空的呀。可不可以像其他正常的類一樣,添加點方法或者構造函數進去呢?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"答案是肯定的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"先看一個整體的方案:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"public record CustUserWithBody(\n String firstName,\n String lastName,\n Address address,\n int age\n) {\n public String fullName(){\n return firstName+ lastName;\n }\n\n public CustUserWithBody{\n if (age < 18) {\n throw new IllegalArgumentException( \"男大當婚,女大當嫁,18歲未到,不許出嫁!\");\n }\n }\n}\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們在record的主題中,定義了一個方法和一個構造函數。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"先看這個方法,在方法中我們可以訪問到record中定義的變量,但是千萬不要嘗試去修改他們,因爲他們是final的,你會得到一個變異錯誤。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"再看這個構造函數,這個構造函數沒有小括號,只有大括號,這種構造函數叫做Compact constructor。你無法在record中定義正常的構造函數,因爲會得到一個編譯錯誤。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在這個Compact constructor中,我們可以對定義的字段進行數據校驗。如上所述。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"JEP 360 Sealed Classes (Preview)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在Java中,類層次結構通過繼承實現代碼的重用,父類的方法可以被許多子類繼承。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"但是,類層次結構的目的並不總是重用代碼。有時,其目的是對域中存在的各種可能性進行建模,例如圖形庫支持的形狀類型或金融應用程序支持的貸款類型。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當以這種方式使用類層次結構時,我們可能需要限制子類集從而來簡化建模。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"因爲我們引入了sealed class或interfaces,這些class或者interfaces只允許被指定的類或者interface進行擴展和實現。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"舉個例子:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"package com.example.geometry;\n\npublic abstract sealed class Shape\n permits Circle, Rectangle, Square {...}\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上面的例子中,我們指定了Shape只允許被Circle, Rectangle, Square來繼承。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上面的例子中並沒有指定類的包名,我們可以這樣寫:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"package com.example.geometry;\n\npublic abstract sealed class Shape \n permits com.example.polar.Circle,\n com.example.quad.Rectangle,\n com.example.quad.simple.Square {...}\n"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"JEP 378 Text Blocks"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Text Blocks是爲了解決在java中輸入多行數據的問題。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"比如:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"String html = \"\\n\" +\n \" \\n\" +\n \"

Hello, world

\\n\" +\n \" \\n\" +\n \"\\n\";\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可以寫成:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"String html = \"\"\"\n \n \n

Hello, world

\n \n \n \"\"\";\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"String query = \"SELECT \\\"EMP_ID\\\", \\\"LAST_NAME\\\" FROM \\\"EMPLOYEE_TB\\\"\\n\" +\n \"WHERE \\\"CITY\\\" = 'INDIANAPOLIS'\\n\" +\n \"ORDER BY \\\"EMP_ID\\\", \\\"LAST_NAME\\\";\\n\";\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可以寫成:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"String query = \"\"\"\n SELECT \"EMP_ID\", \"LAST_NAME\" FROM \"EMPLOYEE_TB\"\n WHERE \"CITY\" = 'INDIANAPOLIS'\n ORDER BY \"EMP_ID\", \"LAST_NAME\";\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"非常的方便。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"總結"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"好了,JDK15的新特性全都介紹完了。希望大家能夠喜歡。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"italic"}],"text":"本文作者:flydean程序那些事"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"italic"}],"text":"本文鏈接:"},{"type":"link","attrs":{"href":"http://www.flydean.com/jdk15-release-new-features/","title":null},"content":[{"type":"text","text":"http://www.flydean.com/jdk15-release-new-features/"}],"marks":[{"type":"italic"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"italic"}],"text":"本文來源:flydean的博客"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"italic"}],"text":"歡迎關注我的公衆號:「程序那些事」最通俗的解讀,最深刻的乾貨,最簡潔的教程,衆多你不知道的小技巧等你來發現!"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章