天天看點

Java 13 明天釋出,最新最全新特性解讀

GitHub 8.2k Star 的Java工程師成神之路 ,不來了解一下嗎? GitHub 8.2k Star 的Java工程師成神之路 ,真的不來了解一下嗎? GitHub 8.2k Star 的Java工程師成神之路 ,真的确定不來了解一下嗎?

2017年8月,JCP執行委員會提出将Java的釋出頻率改為每六個月一次,新的釋出周期嚴格遵循時間點,将在每年的3月份和9月份釋出。

目前,JDK官網上已經可以看到JDK 13的進展,最新版的JDK 13将于2019年9月17日釋出。

Java 13 明天釋出,最新最全新特性解讀

目前,JDK13處于Release-Candidate Phase(釋出候選階段),将于9月17日正式釋出。目前該版本包含的特性已經全部固定,主要包含以下五個:

JEP 350,Dynamic CDS Archives

JEP 351,ZGC: Uncommit Unused Memory

JEP 353,Reimplement the Legacy Socket API

JEP 354: Switch Expressions (Preview)

JEP 355,Text Blocks (Preview)

下面來逐一介紹下這五個重要的特性。

Dynamic CDS Archives

這一特性是在JEP310:Application Class-Data Sharing基礎上擴充而來的,Dynamic CDS Archives中的CDS指的就是Class-Data Sharing。

那麼,這個JEP310是個啥東西呢?

我們知道在同一個實體機/虛拟機上啟動多個JVM時,如果每個虛拟機都單獨裝載自己需要的所有類,啟動成本和記憶體占用是比較高的。是以Java團隊引入了CDS的概念,通過把一些核心類在每個JVM間共享,每個JVM隻需要裝載自己的應用類,啟動時間減少了,另外核心類是共享的,是以JVM的記憶體占用也減少了。

CDS 隻能作用于 Boot Class Loader 加載的類,不能作用于 App Class Loader 或者自定義的 Class Loader 加載的類。

在 Java 10 中,則将 CDS 擴充為 AppCDS,顧名思義,AppCDS 不止能夠作用于 Boot Class Loader了,App Class Loader 和自定義的 Class Loader 也都能夠起作用,大大加大了 CDS 的适用範圍。也就說開發自定義的類也可以裝載給多個JVM共享了。

Java 10中包含的JEP310的通過跨不同Java程序共享公共類中繼資料來減少了記憶體占用和改進了啟動時間。

但是,JEP310中,使用AppCDS的過程還是比較複雜的,需要有三個步驟:

1、決定要 Dump 哪些 Class
2、将類的記憶體 Dump 到歸檔檔案中
3、使用 Dump 出來的歸檔檔案加快應用啟動速度

           

這一次的JDK 13中的JEP 350 ,在JEP310的基礎上,又做了一些擴充。允許在Java應用程式執行結束時動态歸檔類,歸檔類将包括預設的基礎層 CDS(class data-sharing)存檔中不存在的所有已加載的應用程式類和庫類。

也就是說,在Java 13中再使用AppCDS的時候,就不在需要這麼複雜了。

ZGC: Uncommit Unused Memory

在讨論這個問題之前,想先問一個問題,JVM的GC釋放的記憶體會還給作業系統嗎?

GC後的記憶體如何處置,其實是取決于不同的垃圾回收器的。因為把記憶體還給OS,意味着要調整JVM的堆大小,這個過程是比較耗費資源的。

在JDK 11中,Java引入了ZGC,這是一款可伸縮的低延遲垃圾收集器,但是當時隻是實驗性的。并且,ZGC釋放的記憶體是不會還給作業系統的。

Java 13 明天釋出,最新最全新特性解讀

而在Java 13中,JEP 351再次對ZGC做了增強,本次 ZGC 可以将未使用的堆記憶體傳回給作業系統。之是以引入這個特性,是因為如今有很多場景中記憶體是比較昂貴的資源,在以下情況中,将記憶體還給作業系統還是很有必要的:

  • 1、那些需要根據使用量付費的容器
  • 2、應用程式可能長時間處于空閑狀态并與許多其他應用程式共享或競争資源的環境。
  • 3、應用程式在執行期間可能有非常不同的堆空間需求。例如,啟動期間所需的堆可能大于稍後在穩定狀态執行期間所需的堆。

Reimplement the Legacy Socket API

使用易于維護和調試的更簡單、更現代的實作替換 java.net.Socket 和 java.net.ServerSocket API。

java.net.Socket和java.net.ServerSocket的實作非常古老,這個JEP為它們引入了一個現代的實作。現代實作是Java 13中的預設實作,但是舊的實作還沒有删除,可以通過設定系統屬性jdk.net.usePlainSocketImpl來使用它們。

運作一個執行個體化Socket和ServerSocket的類将顯示這個調試輸出。這是預設的(新的):

java -XX:+TraceClassLoading JEP353  | grep Socket
    [0.033s][info   ][class,load] java.net.Socket source: jrt:/java.base
    [0.035s][info   ][class,load] java.net.SocketOptions source: jrt:/java.base
    [0.035s][info   ][class,load] java.net.SocketImpl source: jrt:/java.base
    [0.039s][info   ][class,load] java.net.SocketImpl
$$
Lambda$1/0x0000000800b50840 source: java.net.SocketImpl
    [0.042s][info   ][class,load] sun.net.PlatformSocketImpl source: jrt:/java.base
    [0.042s][info   ][class,load] sun.nio.ch.NioSocketImpl source: jrt:/java.base
    [0.043s][info   ][class,load] sun.nio.ch.SocketDispatcher source: jrt:/java.base
    [0.044s][info   ][class,load] java.net.DelegatingSocketImpl source: jrt:/java.base
    [0.044s][info   ][class,load] java.net.SocksSocketImpl source: jrt:/java.base
    [0.044s][info   ][class,load] java.net.ServerSocket source: jrt:/java.base
    [0.045s][info   ][class,load] jdk.internal.access.JavaNetSocketAccess source: jrt:/java.base
    [0.045s][info   ][class,load] java.net.ServerSocket$1 source: jrt:/java.base           

上面輸出的sun.nio.ch.NioSocketImpl就是新提供的實作。

如果使用舊的實作也是可以的(指定參數jdk.net.usePlainSocketImpl):

$ java -Djdk.net.usePlainSocketImpl -XX:+TraceClassLoading JEP353  | grep Socket
    [0.037s][info   ][class,load] java.net.Socket source: jrt:/java.base
    [0.039s][info   ][class,load] java.net.SocketOptions source: jrt:/java.base
    [0.039s][info   ][class,load] java.net.SocketImpl source: jrt:/java.base
    [0.043s][info   ][class,load] java.net.SocketImpl
$$
Lambda$1/0x0000000800b50840 source: java.net.SocketImpl
    [0.046s][info   ][class,load] sun.net.PlatformSocketImpl source: jrt:/java.base
    [0.047s][info   ][class,load] java.net.AbstractPlainSocketImpl source: jrt:/java.base
    [0.047s][info   ][class,load] java.net.PlainSocketImpl source: jrt:/java.base
    [0.047s][info   ][class,load] java.net.AbstractPlainSocketImpl$1 source: jrt:/java.base
    [0.047s][info   ][class,load] sun.net.ext.ExtendedSocketOptions source: jrt:/java.base
    [0.047s][info   ][class,load] jdk.net.ExtendedSocketOptions source: jrt:/jdk.net
    [0.047s][info   ][class,load] java.net.SocketOption source: jrt:/java.base
    [0.047s][info   ][class,load] jdk.net.ExtendedSocketOptions$ExtSocketOption source: jrt:/jdk.net
    [0.047s][info   ][class,load] jdk.net.SocketFlow source: jrt:/jdk.net
    [0.047s][info   ][class,load] jdk.net.ExtendedSocketOptions$PlatformSocketOptions source: jrt:/jdk.net
    [0.047s][info   ][class,load] jdk.net.ExtendedSocketOptions$PlatformSocketOptions$1 source: jrt:/jdk.net
    [0.048s][info   ][class,load] jdk.net.LinuxSocketOptions source: jrt:/jdk.net
    [0.048s][info   ][class,load] jdk.net.LinuxSocketOptions
$$
Lambda$2/0x0000000800b51040 source: jdk.net.LinuxSocketOptions
    [0.049s][info   ][class,load] jdk.net.ExtendedSocketOptions$1 source: jrt:/jdk.net
    [0.049s][info   ][class,load] java.net.StandardSocketOptions source: jrt:/java.base
    [0.049s][info   ][class,load] java.net.StandardSocketOptions$StdSocketOption source: jrt:/java.base
    [0.051s][info   ][class,load] sun.net.ext.ExtendedSocketOptions
$$
Lambda$3/0x0000000800b51440 source: sun.net.ext.ExtendedSocketOptions
    [0.057s][info   ][class,load] java.net.DelegatingSocketImpl source: jrt:/java.base
    [0.057s][info   ][class,load] java.net.SocksSocketImpl source: jrt:/java.base
    [0.058s][info   ][class,load] java.net.ServerSocket source: jrt:/java.base
    [0.058s][info   ][class,load] jdk.internal.access.JavaNetSocketAccess source: jrt:/java.base
    [0.058s][info   ][class,load] java.net.ServerSocket$1 source: jrt:/java.base
               

上面的結果中,舊的實作java.net.PlainSocketImpl被用到了。

Switch Expressions (Preview)

在JDK 12中引入了Switch表達式作為預覽特性。JEP 354修改了這個特性,它引入了yield語句,用于傳回值。這意味着,switch表達式(傳回值)應該使用yield, switch語句(不傳回值)應該使用break。

在以前,我們想要在switch中傳回内容,還是比較麻煩的,一般文法如下:

int i;
    switch (x) {
        case "1":
            i=1;
            break;
        case "2":
            i=2;
            break;
        default:
            i = x.length();
            break;
    }           

在JDK13中使用以下文法:

int i = switch (x) {
        case "1" -> 1;
        case "2" -> 2;
        default -> {
            int len = args[1].length();
            yield len;
        }
    };           

或者

int i = switch (x) {
        case "1": yield 1;
        case "2": yield 2;
        default: {
            int len = args[1].length();
            yield len;
        }
    };           

在這之後,switch中就多了一個關鍵字用于跳出switch塊了,那就是yield,他用于傳回一個值。和return的差別在于:return會直接跳出目前循環或者方法,而yield隻會跳出目前switch塊。

Text Blocks (Preview)

在JDK 12中引入了Raw String Literals特性,但在釋出之前就放棄了。這個JEP在引入多行字元串文字(text block)在意義上是類似的。

text block,文本塊,是一個多行字元串文字,它避免了對大多數轉義序列的需要,以可預測的方式自動格式化字元串,并在需要時讓開發人員控制格式。

我們以前從外部copy一段文本串到Java中,會被自動轉義,如有一段以下字元串:

<html>
      <body>
          <p>Hello, world</p>
      </body>
    </html>           

将其複制到Java的字元串中,會展示成以下内容:

"<html>\n" +
    "    <body>\n" +
    "        <p>Hello, world</p>\n" +
    "    </body>\n" +
    "</html>\n";           

即被自動進行了轉義,這樣的字元串看起來不是很直覺,在JDK 13中,就可以使用以下文法了:

"""
    <html>
      <body>
          <p>Hello, world</p>
      </body>
    </html>
    """;           

使用“”“作為文本塊的開始符合結束符,在其中就可以放置多行的字元串,不需要進行任何轉義。看起來就十厘清爽了。

如常見的SQL語句:

String query = """
    SELECT `EMP_ID`, `LAST_NAME` FROM `EMPLOYEE_TB`
    WHERE `CITY` = 'INDIANAPOLIS'
    ORDER BY `EMP_ID`, `LAST_NAME`;
""";

           

看起來就比較直覺,清爽了。

總結

以上,就是JDK13中包含的5個特性,能夠改變開發者的編碼風格的主要有Text Blocks和Switch Expressions兩個新特性,但是這兩個特性還處于預覽階段。

而且,JDK13并不是LTS(長期支援)版本,如果你正在使用Java 8(LTS)或者Java 11(LTS),暫時可以不必更新到Java 13.

Java 13 明天釋出,最新最全新特性解讀

參考資料:

https://openjdk.java.net/projects/jdk/13/ https://metebalci.com/blog/what-is-new-in-java-13/ https://www.jianshu.com/p/890196bf529a