在去年的9月26日,Oracle官方宣佈Java11正式發佈,這是Java大版本週期變化後的第一個長期支持版本,很是值得關注。Java9和Java10都在很短的時間內就過渡了,因此,Java11將是一個不可忽視的版本。從時間節點看,JDK11的發佈正好處在JDK8免費更新到期的前夕,同時,JDK八、9也將陸續成爲"歷史版本"。javascript
那麼,關於Java11的新特性到底有哪些呢?容我一一介紹。php
局部類型推斷
什麼是局部類型推斷?css
var str = "helloworld";System.out.println(str);
局部變量類型推斷就是左邊的類型直接使用var定義,而不用寫具體的類型,編譯器能根據右邊的表達式自動推斷類型,如上面的str變量使用var定義,編譯器就能經過右邊的"helloworld"自動推斷出這是一個String類型的變量。java
可是,值得注意的是,這個var並非一個關鍵字,不少同窗看到變量都能使用var來定義,那var還不是關鍵字嗎?事實上,它真的不是一個關鍵字。nginx
int var = 10;System.out.println(var);
例如上面的這段代碼是可以正確運行的,這證實var不是關鍵字。
咱們還能夠經過反編譯來看,例如咱們反編譯這樣一段代碼:typescript
var a = 100;System.out.println(a);
反編譯獲得的結果爲:shell
byte a = 100; System.out.println(a);
從這裏能夠看出,var僅僅是一個語法上的改進,在編譯時期便已經將var轉換爲了對應的變量類型。
然而在使用var定義變量時,必須馬上賦值,例以下面的狀況是錯誤的:bash
var a;
由於在不賦值的狀況下,JVM沒法推斷當前變量的類型。
在類中的成員變量(也叫屬性)不可使用var來定義,例以下面的狀況是錯誤的:微信
class Student{ var name = "小明"; var age = 20;}
var的好處在lambda表達式中體現得淋漓盡致。咱們知道,開啓一個線程可使用lambda表達式來完成:併發
Thread t = new Thread(() -> System.out.println(Thread.currentThread().getName()));t.start();
這是一個無參的lambda表達式形式,咱們再看一個帶參lambda表達式:
String[] arr = { "program", "creek", "is", "a", "java", "site" };Stream<String> stream = Stream.of(arr);stream.forEach(x -> System.out.print(x + "\t"));
這是一個forEach的用法,其中須要用到變量x,由於這裏它自動推斷出了x的類型爲String,因此String被省略了,那麼加上var以後代碼變成這樣:
String[] arr = { "program", "creek", "is", "a", "java", "site" };Stream<String> stream = Stream.of(arr);stream.forEach((var x) -> System.out.print(x + "\t"));
若是僅僅只是這樣寫,卻是沒法看出寫var有什麼優點,反而以爲有點畫蛇添足,可是若是要給lambda表達式變量標註註解的話,那麼這個時候var的做用就體現出來了。
String[] arr = { "program", "creek", "is", "a", "java", "site" };Stream<String> stream = Stream.of(arr);stream.forEach((var x) -> System.out.print(x + "\t"));
可是,咱們從何得知x的類型呢?其實咱們不用知曉,由於var就能自動推斷,因此,var的好處在這裏就體現出來了。
集合中的新API
在Java9以前,咱們要想建立新集合,咱們得這樣作:
List<String> list = new ArrayList<String>();list.add("hello");list.add("world");list.add("java");
建立過程略顯麻煩,那麼如今,咱們能夠經過這樣的方式來建立集合:
List<String> list = List.of("hello","world","java");
可是,請注意了,用這樣的方式來建立的集合,是沒法添加元素的,咱們能夠嘗試着添加一下:
List<String> list = List.of("hello","world","java");list.add("test");
運行結果以下:
Exception in thread "main" java.lang.UnsupportedOperationException at java.base/java.util.ImmutableCollections.uoe(ImmutableCollections.java:71) at java.base/java.util.ImmutableCollections$AbstractImmutableCollection.add(ImmutableCollections.java:75) at com.itcast.TestDemo.main(TestDemo.java:8)
那麼這究竟是爲何呢?咱們經過源碼來分析一下。
首先我麼看看List類的of()方法:
static <E> List<E> of(E e1, E e2, E e3) { return new ImmutableCollections.ListN<>(e1, e2, e3);}
該方法調用了ImmutableCollections類的ListN()生成一個集合並返回,咱們看看ListN的源碼:
static final class ListN<E> extends AbstractImmutableList<E> implements Serializable {
static final List<?> EMPTY_LIST = new ListN<>();
private final E[] elements;
ListN(E... input) { // copy and check manually to avoid TOCTOU "unchecked") ( E[] tmp = (E[])new Object[input.length]; // implicit nullcheck of input for (int i = 0; i < input.length; i++) { tmp[i] = Objects.requireNonNull(input[i]); } elements = tmp; }
public boolean isEmpty() { return size() == 0; }
public int size() { return elements.length; }
public E get(int index) { return elements[index]; }
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { throw new InvalidObjectException("not serial proxy"); }
private Object writeReplace() { return new CollSer(CollSer.IMM_LIST, elements); } }
它是ImmutableCollections類的一個靜態內部類,咱們暫且無論它是如何生成集合的,咱們找找裏面有沒有add()方法,會發現裏面並不存在add()方法,那麼咱們既然可以調用到,那麼add()方法確定在其父類中。最終,在它的父類AbstractImmutableCollection中找到了add()方法:
// all mutating methods throw UnsupportedOperationException public boolean add(E e) { throw uoe(); } public boolean addAll(Collection<? extends E> c) { throw uoe(); } public void clear() { throw uoe(); } public boolean remove(Object o) { throw uoe(); } public boolean removeAll(Collection<?> c) { throw uoe(); } public boolean removeIf(Predicate<? super E> filter) { throw uoe(); } public boolean retainAll(Collection<?> c) { throw uoe(); }
add()方法調用了uoe()方法,而uoe()方法直接拋出了一個異常:
static UnsupportedOperationException uoe() { return new UnsupportedOperationException(); }
會發現,調用uoe()方法的不僅add()方法一個,有關於集合添加、修改、刪除的種種操做都會拋出異常。因此,由of()方法建立的集合是不能夠進行這些相關操做的。
流中的新API
上面集合中說到的of()方法一樣能夠用在流中。
Stream<Integer> stream = Stream.of(1,2,3,4,5);
Stream stream = Stream.of();Stream stream2 = Stream.of(null);
而在上面的兩條語句中,第二條語句會產生空指針異常,固然,咱們不能容許咱們的程序出現這樣的異常,可是你又頗有可能會傳入一個null,這樣的狀況該如何避免呢?從Java9開始,出現了一個新方法:
Stream stream3 = Stream.ofNullable(null);
該方法容許你傳入一個null值,以此避免空指針異常產生。
繼續介紹Stream中的新API。
1.takeWhile
該方法會從流中一直獲取斷定器爲真的元素,一旦遇到元素爲假,就終止處理
Stream<Integer> stream = Stream.of(1, 3, 2, 5, 6, 7);Stream stream2 = stream.takeWhile(t -> t % 2 != 0);stream2.forEach(System.out::println);
這段程序的運行結果:
13
你如果理解了這個方法的意思,這樣的輸出結果就不難理解。由於當獲取到元素2時,斷定器爲假,此時會終止處理,因此後面的元素就不會再去處理。
2.dropWhile()
那麼這方法和takeWhile()方法相反,它會從流中一直丟棄斷定器爲真的元素,一旦遇到元素爲假,就終止處理
Stream<Integer> stream = Stream.of(1, 3, 2, 5, 6, 7);Stream stream2 = stream.dropWhile(t -> t % 2 != 0);stream2.forEach(System.out::println);
因此上面程序段的執行結果爲:
2567
字符串中的新API
1.isBlank()
判斷字符串中的字符是否都爲空白
2.strip()
去除字符串首尾的空白
3.stripTrailing()
去除字符串尾部的空白
4.stripLeading()
去除字符串首部的空白
5.repeat()
複製字符串,能夠傳入一個int類型值來控制複製次數
咱們知道在字符串處理方法中,trim()方法也可以去除字符串首尾的空白,那爲何Oracle還要設計一個重複的方法呢?這必然有它的道理。其實,trim()方法要比strip()方法簡單得多:
/** * Returns a string whose value is this string, with all leading * and trailing space removed, where space is defined * as any character whose codepoint is less than or equal to * {@code 'U+0020'} (the space character). */ public String trim() { String ret = isLatin1() ? StringLatin1.trim(value) : StringUTF16.trim(value); return ret == null ? this : ret; }
經過查閱源碼中對該方法的註釋發現,trim()方法只能去除Unicode碼值小於等於32的空白字符,而32正好指的是空格,那麼對於全角的空格,trim()方法就無能爲力了。因此在功能上,strip()方法更增強大。
HttpAPI
這是Java9開始引入的一個處理HTTP請求的API,該API支持同步和異步,而在Java11中已經爲正式可用狀態。
HttpClient client = HttpClient.newHttpClient();HttpRequest request = HttpRequest.newBuilder(URI.create("http://www.baidu.com")).build();BodyHandler<String> responseBodyHandler = BodyHandlers.ofString();HttpResponse<String> response = client.send(request, responseBodyHandler);String body = response.body();System.out.println(body);
這是一段基本的訪問百度的請求代碼,固然,它還提供了異步請求方式:
HttpClient client = HttpClient.newHttpClient();HttpRequest request = HttpRequest.newBuilder(URI.create("http://www.baidu.com")).build();BodyHandler<String> responseBodyHandler = BodyHandlers.ofString();CompletableFuture<HttpResponse<String>> sendAsync = client.sendAsync(request, responseBodyHandler);HttpResponse<String> response = sendAsync.get();String body = response.body();System.out.println(body);
新版本廢棄了哪些內容
刪除了com.sun.awt.AWTUtilities類
從Oracle JDK中刪除了Lucida字體
Oracle JDK再也不提供任何字體,徹底依賴於操做系統上安裝的字體。刪除了appletviewer啓動器
Epsilon垃圾收集器
JDK上對這個特性的描述是:開發一個處理內存分配但不實現任何實際內存回收機制的GC,一旦可用堆內存用完,JVM就會退出。
咱們能夠來嘗試着使用一下它,首先咱們編寫一段程序:
public class EpsilonTest { public static void main(String[] args) throws Exception { var list = new ArrayList<>(); boolean flag = true; int count = 0; while (flag) { list.add(new Garbage()); if (count++ == 500) { list.clear(); } } }}
class Garbage { private double d1 = 1; private double d2 = 2;
/** * GC在清除本對象時會調用該方法 */ protected void finalize() throws Throwable { System.out.println(this + " collecting"); }}
這是一個無限循環的程序,循環體不斷建立Garbage對象並放入集合,當循環次數達到500時將集合清空,此時的500個對象均爲垃圾,會被GC清理,清理時調用finalize()方法打印信息。運行這段程序,結果以下:
...com.itcast.Garbage@1e9c634c collectingjava.lang.OutOfMemoryError: Java heap space at com.itcast.EpsilonTest.main(EpsilonTest.java:11)com.itcast.Garbage@1174213e collectingcom.itcast.Garbage@2029a4b8 collecting...
當程序執行到某一刻時,內存溢出,程序終止。
如今咱們來使用一下Epsilon,右鍵選擇類文件,在Run As右側選擇Run Configurations:
如今咱們將默認的GC換爲了Epsilon,再來看看運行結果:
Terminating due to java.lang.OutOfMemoryError: Java heap space
會發現,控制檯只輸出了這麼一句,說明被清除的集合中的對象並無被回收,並且內存溢出的速度也很是快,這說明該GC是並不會回收垃圾,那麼它有什麼做用呢?
它提供徹底被動的GC實現,具備有限的分配限制和儘量低的延遲開銷,但代價是內存佔用和內存吞吐量,它的主要用途有如下幾個方面:
性能測試(它能夠幫助過濾掉GC引發的性能假象)
內存壓力測試
很是短的JOB任務
VM接口測試
ZGC垃圾回收器
有人說這是JDK11最爲矚目的特性,沒有之一,是最重磅的升級,那麼ZGC的優點在哪裏呢?
GC暫停時間不會超過10毫秒
既能處理幾百兆的小堆,也能處理幾個T的大堆
和G1相比,應用吞吐能力不會降低超過15%
爲將來的GC功能和利用colord指針以及Load barriers優化奠基了基礎
ZGC是一個併發、基於region、壓縮型的垃圾收集器,只有root掃描階段會STW(stop the world,中止全部線程),所以ZGC的停頓時間不會隨着堆的增加和存活對象的增加而變長。
用法:-XX:UnlockExperimentalVMOptions -XX:+UseZGC
雖然功能如此強大,但很遺憾的是,在Windows系統的JDK中並無提供ZGC,因此也就沒有辦法嚐鮮了。
Flight Recorder
這是一個記錄儀,用於診斷程序運行過程,那麼在以前這是一個商業版的特性,是要收費的,從Java11開始,Fight Recorder免費提供使用並開源。它能夠導出事件到文件中,以後能夠用Java Mission Control來分析,也能夠在應用啓動時配置java -XX:StartFlightRecording或者在應用啓動以後使用jcmd來錄製,好比:
jcmd <pid> JFR.start jcmd <pid> JFR.dump filename= jcmd <pid> JFR.stop
其它
在Java11中,支持一個命令編譯運行文件,在以前的版本中,咱們要想運行一個Java程序,首先得用javac指令編譯,而後用java指令運行。而在新版本中,咱們直接使用java指令便可完成編譯運行操做。
在Unicode10版本中,增長了8518個字符,總計達到了136690個字符,這已經超出了char類型的數值範圍,因此在Java11中,新增了CharacterData,使用四個字節來處理字符。
那麼有關Java11的新特性就介紹到這裏。
本文分享自微信公衆號 - 碼視界(otc_18679428729)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。