天天看點

【譯】Java 8的新特性—終極版1. 簡介2. Java語言的新特性3. Java編譯器的新特性4. Java官方庫的新特性5. 新的Java工具6. JVM的新特性7. 結論8. 參考資料

【譯】Java 8的新特性—終極版1. 簡介2. Java語言的新特性3. Java編譯器的新特性4. Java官方庫的新特性5. 新的Java工具6. JVM的新特性7. 結論8. 參考資料

java 8

這個教程包含java開發者經常面對的幾類問題:

語言

編譯器

工具

運作時(jvm)

java 8是java的一個重大版本,有人認為,雖然這些新特性領java開發人員十分期待,但同時也需要花不少精力去學習。在這一小節中,我們将介紹java 8的大部分新特性。

lambda的設計耗費了很多時間和很大的社群力量,最終找到一種折中的實作方案,可以實作簡潔而緊湊的語言結構。最簡單的lambda表達式可由逗号分隔的參數清單、->符号和語句塊組成,例如:

在上面這個代碼中的參數e的類型是由編譯器推理得出的,你也可以顯式指定該參數的類型,例如:

如果lambda表達式需要更複雜的語句塊,則可以使用花括号将該語句塊括起來,類似于java中的函數體,例如:

lambda表達式可以引用類成員和局部變量(會将這些變量隐式得轉換成final的),例如下列兩個代碼塊的效果完全相同:

lambda表達式有傳回值,傳回值的類型也由編譯器推理得出。如果lambda表達式中的語句塊隻有一行,則可以不用使用return語句,下列兩個代碼片段效果相同:

預設方法和抽象方法之間的差別在于抽象方法需要實作,而預設方法不需要。接口提供的預設方法會被接口的實作類繼承或者覆寫,例子代碼如下:

defaulable接口使用關鍵字default定義了一個預設方法notrequired()。defaultableimpl類實作了這個接口,同時預設繼承了這個接口中的預設方法;overridableimpl類也實作了這個接口,但覆寫了該接口的預設方法,并提供了一個不同的實作。

java 8帶來的另一個有趣的特性是在接口中可以定義靜态方法,例子代碼如下:

下面的代碼片段整合了預設方法和靜态方法的使用場景:

這段代碼的輸出結果如下:

由于jvm上的預設方法的實作在位元組碼層面提供了支援,是以效率非常高。預設方法允許在不打破現有繼承體系的基礎上改進接口。該特性在官方庫中的應用是:給java.util.collection接口添加新方法,如stream()、parallelstream()、foreach()和removeif()等等。

方法引用使得開發者可以直接引用現存的方法、java類的構造方法或者執行個體對象。方法引用和lambda表達式配合使用,使得java類的構造方法看起來緊湊而簡潔,沒有很多複雜的模闆代碼。

西門的例子中,car類是不同方法引用的例子,可以幫助讀者區分四種類型的方法引用。

第一種方法引用的類型是構造器引用,文法是class::new,或者更一般的形式:class<t>::new。注意:這個構造器沒有參數。

第二種方法引用的類型是靜态方法引用,文法是class::static_method。注意:這個方法接受一個car類型的參數。

第三種方法引用的類型是某個類的成員方法的引用,文法是class::method,注意,這個方法沒有定義入參:

第四種方法引用的類型是某個執行個體對象的成員方法的引用,文法是instance::method。注意:這個方法接受一個car類型的參數:

運作上述例子,可以在控制台看到如下輸出(car執行個體可能不同):

在java 8中使用@repeatable注解定義重複注解,實際上,這并不是語言層面的改進,而是編譯器做的一個trick,底層的技術仍然相同。可以利用下面的代碼說明:

正如我們所見,這裡的filter類使用@repeatable(filters.class)注解修飾,而filters是存放filter注解的容器,編譯器盡量對開發者屏蔽這些細節。這樣,filterable接口可以用兩個filter注解注釋(這裡并沒有提到任何關于filters的資訊)。

另外,反射api提供了一個新的方法:getannotationsbytype(),可以傳回某個類型的重複注解,例如<code>filterable.class.getannoation(filters.class)</code>将傳回兩個filter執行個體,輸出到控制台的内容如下所示:

java 8編譯器在類型推斷方面有很大的提升,在很多場景下編譯器可以推導出某個參數的資料類型,進而使得代碼更為簡潔。例子代碼如下:

下列代碼是value&lt;string&gt;類型的應用:

參數value.defaultvalue()的類型由編譯器推導得出,不需要顯式指明。在java 7中這段代碼會有編譯錯誤,除非使用<code>value.&lt;string&gt;defaultvalue()</code>。

java 8拓寬了注解的應用場景。現在,注解幾乎可以使用在任何元素上:局部變量、接口類型、超類和接口實作類,甚至可以用在函數的異常定義上。下面是一些例子:

elementtype.type_user和elementtype.type_parameter是java 8新增的兩個注解,用于描述注解的使用場景。java 語言也做了對應的改變,以識别這些新增的注解。

在java 8中這個特性是預設關閉的,是以如果不帶-parameters參數編譯上述代碼并運作,則會輸出如下結果:

如果帶-parameters參數,則會輸出如下結果(正确的結果):

如果你使用maven進行項目管理,則可以在maven-compiler-plugin編譯器的配置項中配置-parameters參數:

java 8增加了很多新的工具類(date/time類),并擴充了現存的工具類,以支援現代的并發程式設計、函數式程式設計等。

接下來看一點使用optional的例子:可能為空的值或者某個類型的值:

如果optional執行個體持有一個非空值,則ispresent()方法傳回true,否則傳回false;orelseget()方法,optional執行個體持有null,則可以接受一個lambda表達式生成的預設值;map()方法可以将現有的opetional執行個體的值轉換成新的值;orelse()方法與orelseget()方法類似,但是在持有null的時候傳回傳入的預設值。

上述代碼的輸出結果如下:

再看下另一個簡單的例子:

這個例子的輸出是:

steam api極大得簡化了集合操作(後面我們會看到不止是集合),首先看下這個叫task的類:

task類有一個分數(或僞複雜度)的概念,另外還有兩種狀态:open或者closed。現在假設有一個task集合:

首先看一個問題:在這個task集合中一共有多少個open狀态的點?在java 8之前,要解決這個問題,則需要使用foreach循環周遊task集合;但是在java 8中可以利用steams解決:包括一系列元素的清單,并且支援順序和并行處理。

運作這個方法的控制台輸出是:

這裡有很多知識點值得說。首先,tasks集合被轉換成steam表示;其次,在steam上的filter操作會過濾掉所有closed的task;第三,maptoint操作基于每個task執行個體的task::getpoints方法将task流轉換成integer集合;最後,通過sum方法計算總和,得出最後的結果。

中間操作會傳回一個新的steam——執行一個中間操作(例如filter)并不會執行實際的過濾操作,而是建立一個新的steam,并将原steam中符合條件的元素放入新建立的steam。

晚期操作(例如foreach或者sum),會周遊steam并得出結果或者附帶結果;在執行晚期操作之後,steam處理線已經處理完畢,就不能使用了。在幾乎所有情況下,晚期操作都是立刻對steam進行周遊。

steam的另一個價值是創造性地支援并行處理(parallel processing)。對于上述的tasks集合,我們可以用下面的代碼計算所有任務的點數之和:

這裡我們使用parallel方法并行處理所有的task,并使用reduce方法計算最終的結果。控制台輸出如下:

對于一個集合,經常需要根據某些條件對其中的元素分組。利用steam提供的api可以很快完成這類任務,代碼如下:

控制台的輸出如下:

最後一個關于tasks集合的例子問題是:如何計算集合中每個任務的點數在集合中所占的比重,具體處理的代碼如下:

控制台輸出結果如下:

最後,正如之前所說,steam api不僅可以作用于java集合,傳統的io操作(從檔案或者網絡一行一行得讀取資料)可以受益于steam處理,這裡有一個小例子:

stream的方法onclose 傳回一個等價的有額外句柄的stream,當stream的close()方法被調用的時候這個句柄會被執行。stream api、lambda表達式還有接口預設方法和靜态方法支援的方法引用,是java 8對軟體開發的現代範式的響應。

我們接下來看看java.time包中的關鍵類和各自的使用例子。首先,clock類使用時區來傳回目前的納秒時間和日期。clock可以替代system.currenttimemillis()和timezone.getdefault()。

這個例子的輸出結果是:

第二,關注下localdate和localtime類。localdate僅僅包含iso-8601月曆系統中的日期部分;localtime則僅僅包含該月曆系統中的時間部分。這兩個類的對象都可以使用clock對象建構得到。

上述例子的輸出結果如下:

上述這個例子的輸出結果如下:

如果你需要特定時區的data/time資訊,則可以使用zonedatetime,它儲存有iso-8601日期系統的日期和時間,而且有時區資訊。下面是一些使用不同時區的例子:

最後看下duration類,它持有的時間精确到秒和納秒。這使得我們可以很容易得計算兩個日期之間的不同,例子代碼如下:

這個例子用于計算2014年4月16日和2015年4月16日之間的天數和小時數,輸出結果如下:

這個代碼的輸出結果如下:

這個例子的輸出結果如下:

新的base64api也支援url和mine的編碼解碼。

(base64.geturlencoder() / base64.geturldecoder(), base64.getmimeencoder() / base64.getmimedecoder())。

java8版本新增了很多新的方法,用于支援并行數組處理。最重要的方法是parallelsort(),可以顯著加快多核機器上的數組排序。下面的例子論證了parallexxxx系列的方法:

上述這些代碼使用parallelsetall()方法生成20000個随機數,然後使用parallelsort()方法進行排序。這個程式會輸出亂序數組和排序數組的前10個元素。上述例子的代碼輸出的結果是:

java 8還添加了新的java.util.concurrent.locks.stampedlock類,用于支援基于容量的鎖——該鎖有三個模型用于支援讀寫操作(可以把這個鎖當做是java.util.concurrent.locks.readwritelock的替代者)。

在java.util.concurrent.atomic包中也新增了不少工具類,列舉如下:

doubleaccumulator

doubleadder

longaccumulator

longadder

java 8提供了一些新的指令行工具,這部分會講解一些對開發者最有用的工具。

jjs是一個基于标準nashorn引擎的指令行工具,可以接受js源碼并執行。例如,我們寫一個func.js檔案,内容如下:

可以在指令行中執行這個指令:<code>jjs func.js</code>,控制台輸出結果是:

jdeps是一個相當棒的指令行工具,它可以展示包層級和類層級的java類依賴關系,它以.class檔案、目錄或者jar檔案為輸入,然後會把依賴關系輸出到控制台。

這個指令會輸出很多結果,我們僅看下其中的一部分:依賴關系按照包分組,如果在classpath上找不到依賴,則顯示"not found".

通過為開發者提供很多能夠提高生産力的特性,java 8使得java平台前進了一大步。現在還不太适合将java 8應用在生産系統中,但是在之後的幾個月中java 8的應用率一定會逐漸提高(ps:原文時間是2014年5月9日,現在在很多公司java 8已經成為主流,我司由于體量太大,現在也在一點點上java 8,雖然慢但是好歹在更新了)。作為開發者,現在應該學習一些java 8的知識,為更新做好準備。

<a href="http://www.oracle.com/technetwork/java/javase/8-whats-new-2157071.html" target="_blank">what’s new in jdk 8</a>

<a href="http://docs.oracle.com/javase/tutorial/" target="_blank">the java tutorials</a>

<a href="http://blog.arungupta.me/2014/03/wildfly8-jdk8-netbeans8-javaee7-excellent-combo-enterprise-java/" target="_blank">wildfly 8, jdk 8, netbeans 8, java ee</a>

<a href="http://winterbe.com/posts/2014/03/16/java-8-tutorial/" target="_blank">java 8 tutorial</a>

<a href="http://marxsoftware.blogspot.ca/2014/03/jdeps.html" target="_blank">jdk 8 command-line static dependency checker</a>

<a href="http://marxsoftware.blogspot.ca/2014/03/illuminating-javadoc-of-jdk-8.html" target="_blank">the illuminating javadoc of jdk</a>

<a href="http://blog.jooq.org/2014/04/04/java-8-friday-the-dark-side-of-java-8/" target="_blank">the dark side of java 8</a>

<a href="http://www.eclipse.org/downloads/java8/" target="_blank">installing java™ 8 support in eclipse kepler sr2</a>

<a href="http://www.baeldung.com/java8" target="_blank">java 8</a>

<a href="http://www.oracle.com/technetwork/articles/java/jf14-nashorn-2126515.html" target="_blank">oracle nashorn. a next-generation javascript engine for the jvm</a>

<a href="http://www.importnew.com/6675.html">— 身為一名java程式員,大家可能都有這樣的經曆:調用一個方法得到了傳回值卻不能直接将傳回值作為參數去調用别的方法。我們首先要判斷這個傳回值是否為null,隻有在非空的前提下才能将其作為其他方法的參數。這正是一些類似guava的外部api試圖解決的問題。本文深入介紹了java 8 optional類是如何解決該問題。</a>

<a href="http://wendal.net/2014/03/30.html">— jdk  之前, eclipse自帶的ecj編譯器,同本地變量表,把方法參數的名字,放在最前面,使其編譯出來的class的名字看推測.  而 jdk8把這種行為規範化</a>

<a href="http://www.infoq.com/cn/articles/spring-4-java-8">— 具有衆多新特性和函數庫的java 8釋出之後,spring 4.x已經支援其中的大部分。有些java 8的新特性對spring無影響,可以直接使用,但另有些新特性需要spring的支援。本文将帶您浏覽spring 4.0和4.1已經支援的java 8新特性。</a>