java8新特性介紹

目錄結(jié)構(gòu)

  1. 介紹

  2. Java語言的新特性

    2.1 Lambdas表達式與Functional接口

    2.2 接口的默認與靜態(tài)方法

    2.3 方法引用

    2.4 重復(fù)注解

    2.5 更好的類型推測機制

    2.6 擴展注解的支持

  3. Java編譯器的新特性

    3.1 參數(shù)名字

  4. Java 類庫的新特性

    4.1 Optional

    4.2 Streams

    4.3 Date/Time API (JSR 310)

    4.4 JavaScript引擎Nashorn

    4.5 Base64

    4.6 并行(parallel)數(shù)組

    4.7 并發(fā)(Concurrency)

  5. 新增的Java工具

    5.1 Nashorn引擎: jjs

    5.2 類依賴分析器: jdeps

  6. Java虛擬機(JVM)的新特性

  7. 總結(jié)

  8. 更多資源

1.介紹

毫無疑問坑雅,Java 8發(fā)行版是自Java 5(發(fā)行于2004缝左,已經(jīng)過了相當(dāng)一段時間了)以來最具革命性的版本皇帮。Java 8 為Java語言巩梢、編譯器、類庫首尼、開發(fā)工具與JVM(Java虛擬機)帶來了大量新特性耙替。在這篇教程中,我們將一一探索這些變化胶逢,并用真實的例子說明它們適用的場景厅瞎。

這篇教程由以下幾部分組成饰潜,它們分別涉及到Java平臺某一特定方面的內(nèi)容:

  • Java語言
  • 編譯器
  • 類庫
  • 工具
  • Java運行時(JVM)

2.Java語言的新特性

不管怎么說,Java 8都是一個變化巨大的版本和簸。你可能認為Java 8耗費了大量的時間才得以完成是為了實現(xiàn)了每個Java程序員所期待的特性彭雾。在這個小節(jié)里,我們將會涉及到這些特性的大部分锁保。

2.1 Lambda表達式與Functional接口

Lambda表達式(也稱為閉包)是整個Java 8發(fā)行版中最受期待的在Java語言層面上的改變薯酝,Lambda允許把函數(shù)作為一個方法的參數(shù)(函數(shù)作為參數(shù)傳遞進方法中),或者把代碼看成數(shù)據(jù):函數(shù)式程序員對這一概念非常熟悉爽柒。在JVM平臺上的很多語言(Groovy吴菠,Scala,……)從一開始就有Lambda浩村,但是Java程序員不得不使用毫無新意的匿名類來代替lambda橄务。

關(guān)于Lambda設(shè)計的討論占用了大量的時間與社區(qū)的努力⊙鳎可喜的是蜂挪,最終找到了一個平衡點,使得可以使用一種即簡潔又緊湊的新方式來構(gòu)造Lambdas嗓化。在最簡單的形式中棠涮,一個lambda可以由用逗號分隔的參數(shù)列表、–>符號與函數(shù)體三部分表示刺覆。例如:

<pre class="brush: java; gutter: true; first-line: 1; highlight: []; html-script: false" style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 14px; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">Arrays.asList( "a", "b", "d" ).forEach( e -> System.out.println( e ) );
</pre>

請注意參數(shù)e的類型是由編譯器推測出來的严肪。同時,你也可以通過把參數(shù)類型與參數(shù)包括在括號中的形式直接給出參數(shù)的類型:

<pre class="brush: java; gutter: true; first-line: 1; highlight: []; html-script: false" style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 14px; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">Arrays.asList( "a", "b", "d" ).forEach( ( String e ) -> System.out.println( e ) );
</pre>

在某些情況下lambda的函數(shù)體會更加復(fù)雜谦屑,這時可以把函數(shù)體放到在一對花括號中驳糯,就像在Java中定義普通函數(shù)一樣。例如:

<pre class="brush: java; gutter: true; first-line: 1; highlight: []; html-script: false" style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 14px; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">Arrays.asList( "a", "b", "d" ).forEach( e -> {
System.out.print( e );
System.out.print( e );
} );
</pre>

Lambda可以引用類的成員變量與局部變量(如果這些變量不是final的話氢橙,它們會被隱含的轉(zhuǎn)為final酝枢,這樣效率更高)。例如悍手,下面兩個代碼片段是等價的:

<pre class="brush: java; gutter: true; first-line: 1; highlight: []; html-script: false" style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 14px; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">String separator = ",";
Arrays.asList( "a", "b", "d" ).forEach(
( String e ) -> System.out.print( e + separator ) );
</pre>

和:

<pre class="brush: java; gutter: true; first-line: 1; highlight: []; html-script: false" style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 14px; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">final String separator = ",";
Arrays.asList( "a", "b", "d" ).forEach(
( String e ) -> System.out.print( e + separator ) );
</pre>

Lambda可能會返回一個值帘睦。返回值的類型也是由編譯器推測出來的。如果lambda的函數(shù)體只有一行的話坦康,那么沒有必要顯式使用return語句竣付。下面兩個代碼片段是等價的:

<pre class="brush: java; gutter: true; first-line: 1; highlight: []; html-script: false" style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 14px; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">Arrays.asList( "a", "b", "d" ).sort( ( e1, e2 ) -> e1.compareTo( e2 ) );
</pre>

和:

<pre class="brush: java; gutter: true; first-line: 1; highlight: []; html-script: false" style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 14px; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">Arrays.asList( "a", "b", "d" ).sort( ( e1, e2 ) -> {
int result = e1.compareTo( e2 );
return result;
} );
</pre>

語言設(shè)計者投入了大量精力來思考如何使現(xiàn)有的函數(shù)友好地支持lambda。最終采取的方法是:增加函數(shù)式接口的概念滞欠。函數(shù)式接口就是一個具有一個方法的普通接口古胆。像這樣的接口,可以被隱式轉(zhuǎn)換為lambda表達式筛璧。java.lang.Runnable與java.util.concurrent.Callable是函數(shù)式接口最典型的兩個例子逸绎。在實際使用過程中妖滔,函數(shù)式接口是容易出錯的:如有某個人在接口定義中增加了另一個方法,這時桶良,這個接口就不再是函數(shù)式的了座舍,并且編譯過程也會失敗。為了克服函數(shù)式接口的這種脆弱性并且能夠明確聲明接口作為函數(shù)式接口的意圖陨帆,Java 8增加了一種特殊的注解@FunctionalInterface(Java 8中所有類庫的已有接口都添加了@FunctionalInterface注解)曲秉。讓我們看一下這種函數(shù)式接口的定義:

<pre class="brush: java; gutter: true; first-line: 1; highlight: []; html-script: false" style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 14px; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">@FunctionalInterface
public interface Functional {
void method();
}
</pre>

需要記住的一件事是:默認方法與靜態(tài)方法并不影響函數(shù)式接口的契約,可以任意使用:

<pre class="brush: java; gutter: true; first-line: 1; highlight: []; html-script: false" style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 14px; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">@FunctionalInterface
public interface FunctionalDefaultMethods {
void method();

default void defaultMethod() {            
}        

}
</pre>

Lambda是Java 8最大的賣點疲牵。它具有吸引越來越多程序員到Java平臺上的潛力承二,并且能夠在純Java語言環(huán)境中提供一種優(yōu)雅的方式來支持函數(shù)式編程。更多詳情可以參考官方文檔纲爸。

2.2 接口的默認方法與靜態(tài)方法

Java 8用默認方法與靜態(tài)方法這兩個新概念來擴展接口的聲明亥鸠。默認方法使接口有點像Traits(Scala中特征(trait)類似于Java中的Interface,但它可以包含實現(xiàn)代碼识啦,也就是目前Java8新增的功能)负蚊,但與傳統(tǒng)的接口又有些不一樣,它允許在已有的接口中添加新方法颓哮,而同時又保持了與舊版本代碼的兼容性家妆。

默認方法與抽象方法不同之處在于抽象方法必須要求實現(xiàn),但是默認方法則沒有這個要求冕茅。相反伤极,每個接口都必須提供一個所謂的默認實現(xiàn),這樣所有的接口實現(xiàn)者將會默認繼承它(如果有必要的話姨伤,可以覆蓋這個默認實現(xiàn))哨坪。讓我們看看下面的例子:

<pre class="brush: java; gutter: true; first-line: 1; highlight: []; html-script: false" style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 14px; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">private interface Defaulable {
// Interfaces now allow default methods, the implementer may or
// may not implement (override) them.
default String notRequired() {
return "Default implementation";
}
}

private static class DefaultableImpl implements Defaulable {
}

private static class OverridableImpl implements Defaulable {
@Override
public String notRequired() {
return "Overridden implementation";
}
}
</pre>

Defaulable接口用關(guān)鍵字default聲明了一個默認方法notRequired(),Defaulable接口的實現(xiàn)者之一DefaultableImpl實現(xiàn)了這個接口乍楚,并且讓默認方法保持原樣当编。Defaulable接口的另一個實現(xiàn)者OverridableImpl用自己的方法覆蓋了默認方法。

Java 8帶來的另一個有趣的特性是接口可以聲明(并且可以提供實現(xiàn))靜態(tài)方法炊豪。例如:

<pre class="brush: java; gutter: true; first-line: 1; highlight: []; html-script: false" style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 14px; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">private interface DefaulableFactory {
// Interfaces now allow static methods
static Defaulable create( Supplier< Defaulable > supplier ) {
return supplier.get();
}
}
</pre>

下面的一小段代碼片段把上面的默認方法與靜態(tài)方法黏合到一起凌箕。

<pre class="brush: java; gutter: true; first-line: 1; highlight: []; html-script: false" style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 14px; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">public static void main( String[] args ) {
Defaulable defaulable = DefaulableFactory.create( DefaultableImpl::new );
System.out.println( defaulable.notRequired() );

defaulable = DefaulableFactory.create( OverridableImpl::new );
System.out.println( defaulable.notRequired() );

}
</pre>

這個程序的控制臺輸出如下:

<pre class="brush:shell; gutter: true; first-line: 1; highlight: []; html-script: false" style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 14px; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">Default implementation
Overridden implementation
</pre>

在JVM中,默認方法的實現(xiàn)是非常高效的词渤,并且通過字節(jié)碼指令為方法調(diào)用提供了支持。默認方法允許繼續(xù)使用現(xiàn)有的Java接口串绩,而同時能夠保障正常的編譯過程缺虐。這方面好的例子是大量的方法被添加到j(luò)ava.util.Collection接口中去:stream(),parallelStream()礁凡,forEach()高氮,removeIf(),……

盡管默認方法非常強大剪芍,但是在使用默認方法時我們需要小心注意一個地方:在聲明一個默認方法前塞淹,請仔細思考是不是真的有必要使用默認方法,因為默認方法會帶給程序歧義罪裹,并且在復(fù)雜的繼承體系中容易產(chǎn)生編譯錯誤饱普。更多詳情請參考官方文檔

2.3 方法引用

方法引用提供了非常有用的語法,可以直接引用已有Java類或?qū)ο螅▽嵗┑姆椒ɑ驑?gòu)造器状共。與lambda聯(lián)合使用套耕,方法引用可以使語言的構(gòu)造更緊湊簡潔,減少冗余代碼峡继。

下面冯袍,我們以定義了4個方法的Car這個類作為例子,區(qū)分Java中支持的4種不同的方法引用碾牌。

<pre class="brush: java; gutter: true; first-line: 1; highlight: []; html-script: false" style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 14px; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">public static class Car {
public static Car create( final Supplier< Car > supplier ) {
return supplier.get();
}

public static void collide( final Car car ) {
    System.out.println( "Collided " + car.toString() );
}

public void follow( final Car another ) {
    System.out.println( "Following the " + another.toString() );
}

public void repair() {   
    System.out.println( "Repaired " + this.toString() );
}

}
</pre>

第一種方法引用是構(gòu)造器引用康愤,它的語法是Class::new,或者更一般的Class< T >::new舶吗。請注意構(gòu)造器沒有參數(shù)翘瓮。

<pre class="brush: java; gutter: true; first-line: 1; highlight: []; html-script: false" style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 14px; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">final Car car = Car.create( Car::new );
final List< Car > cars = Arrays.asList( car );
</pre>

第二種方法引用是靜態(tài)方法引用,它的語法是Class::static_method裤翩。請注意這個方法接受一個Car類型的參數(shù)资盅。

<pre class="brush: java; gutter: true; first-line: 1; highlight: []; html-script: false" style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 14px; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">cars.forEach( Car::collide );
</pre>

第三種方法引用是特定類的任意對象的方法引用廷蓉,它的語法是Class::method俯渤。請注意饲化,這個方法沒有參數(shù)癌蓖。

<pre class="brush: java; gutter: true; first-line: 1; highlight: []; html-script: false" style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 14px; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">cars.forEach( Car::repair );
</pre>

最后哪痰,第四種方法引用是特定對象的方法引用搀捷,它的語法是instance::method茂装。請注意怎诫,這個方法接受一個Car類型的參數(shù)

<pre class="brush: java; gutter: true; first-line: 1; highlight: []; html-script: false" style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 14px; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">final Car police = Car.create( Car::new );
cars.forEach( police::follow );
</pre>

運行上面的Java程序在控制臺上會有下面的輸出(Car的實例可能不一樣):

<pre class="brush: java; gutter: true; first-line: 1; highlight: []; html-script: false" style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 14px; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">Collided com.javacodegeeks.java8.method.references.MethodReferencesCar@7a81197d Repaired com.javacodegeeks.java8.method.references.MethodReferencesCar@7a81197d
Following the com.javacodegeeks.java8.method.references.MethodReferences$Car@7a81197d
</pre>

關(guān)于方法引用的更多詳情請參考官方文檔伦籍。

2.4 重復(fù)注解

自從Java 5引入了注解機制蓝晒,這一特性就變得非常流行并且廣為使用。然而帖鸦,使用注解的一個限制是相同的注解在同一位置只能聲明一次芝薇,不能聲明多次。Java 8打破了這條規(guī)則作儿,引入了重復(fù)注解機制洛二,這樣相同的注解可以在同一地方聲明多次。

重復(fù)注解機制本身必須用@Repeatable注解。事實上晾嘶,這并不是語言層面上的改變妓雾,更多的是編譯器的技巧,底層的原理保持不變垒迂。讓我們看一個快速入門的例子:

<pre class="brush: java; gutter: true; first-line: 1; highlight: []; html-script: false" style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 14px; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">package com.javacodegeeks.java8.repeatable.annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

public class RepeatingAnnotations {
@Target( ElementType.TYPE )
@Retention( RetentionPolicy.RUNTIME )
public @interface Filters {
Filter[] value();
}

@Target( ElementType.TYPE )
@Retention( RetentionPolicy.RUNTIME )
@Repeatable( Filters.class )
public @interface Filter {
    String value();
};

@Filter( "filter1" )
@Filter( "filter2" )
public interface Filterable {        
}

public static void main(String[] args) {
    for( Filter filter: Filterable.class.getAnnotationsByType( Filter.class ) ) {
        System.out.println( filter.value() );
    }
}

}
</pre>

正如我們看到的械姻,這里有個使用@Repeatable( Filters.class )注解的注解類Filter,F(xiàn)ilters僅僅是Filter注解的數(shù)組机断,但Java編譯器并不想讓程序員意識到Filters的存在楷拳。這樣,接口Filterable就擁有了兩次Filter(并沒有提到Filter)注解毫缆。

同時唯竹,反射相關(guān)的API提供了新的函數(shù)getAnnotationsByType()來返回重復(fù)注解的類型(請注意Filterable.class.getAnnotation( Filters.class )經(jīng)編譯器處理后將會返回Filters的實例)。

程序輸出結(jié)果如下:

<pre class="brush:shell; gutter: true; first-line: 1; highlight: []; html-script: false" style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 14px; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">filter1
filter2
</pre>

更多詳情請參考官方文檔

2.5 更好的類型推測機制

Java 8在類型推測方面有了很大的提高苦丁。在很多情況下浸颓,編譯器可以推測出確定的參數(shù)類型,這樣就能使代碼更整潔旺拉。讓我們看一個例子:

<pre class="brush: java; gutter: true; first-line: 1; highlight: []; html-script: false" style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 14px; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">package com.javacodegeeks.java8.type.inference;

public class Value< T > {
public static< T > T defaultValue() {
return null;
}

public T getOrDefault( T value, T defaultValue ) {
    return ( value != null ) ? value : defaultValue;
}

}
</pre>

這里是Value< String >類型的用法产上。

<pre class="brush: java; gutter: true; first-line: 1; highlight: []; html-script: false" style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 14px; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">package com.javacodegeeks.java8.type.inference;

public class TypeInference {
public static void main(String[] args) {
final Value< String > value = new Value<>();
value.getOrDefault( "22", Value.defaultValue() );
}
}
</pre>

Value.defaultValue()的參數(shù)類型可以被推測出,所以就不必明確給出蛾狗。在Java 7中晋涣,相同的例子將不會通過編譯,正確的書寫方式是 Value.< String >defaultValue()沉桌。

2.6 擴展注解的支持

Java 8擴展了注解的上下文⌒蝗担現(xiàn)在幾乎可以為任何東西添加注解:局部變量、泛型類留凭、父類與接口的實現(xiàn)佃扼,就連方法的異常也能添加注解。下面演示幾個例子:

<pre class="brush: java; gutter: true; first-line: 1; highlight: []; html-script: false" style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 14px; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">package com.javacodegeeks.java8.annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.ArrayList;
import java.util.Collection;

public class Annotations {
@Retention( RetentionPolicy.RUNTIME )
@Target( { ElementType.TYPE_USE, ElementType.TYPE_PARAMETER } )
public @interface NonEmpty {
}

public static class Holder< @NonEmpty T > extends @NonEmpty Object {
    public void method() throws @NonEmpty Exception {           
    }
}

@SuppressWarnings( "unused" )
public static void main(String[] args) {
    final Holder< String > holder = new @NonEmpty Holder< String >();       
    @NonEmpty Collection< @NonEmpty String > strings = new ArrayList<>();       
}

}
</pre>

ElementType.TYPE_USE和ElementType.TYPE_PARAMETER是兩個新添加的用于描述適當(dāng)?shù)淖⒔馍舷挛牡脑仡愋桶埂T贘ava語言中兼耀,注解處理API也有小的改動來識別新增的類型注解。

3. Java編譯器的新特性

3.1 參數(shù)名字

很長一段時間里求冷,Java程序員一直在發(fā)明不同的方式使得方法參數(shù)的名字能保留在Java字節(jié)碼中瘤运,并且能夠在運行時獲取它們(比如,Paranamer類庫)匠题。最終拯坟,在Java 8中把這個強烈要求的功能添加到語言層面(通過反射API與Parameter.getName()方法)與字節(jié)碼文件(通過新版的javac的–parameters選項)中。

<pre class="brush: java; gutter: true; first-line: 1; highlight: []; html-script: false" style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 14px; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">package com.javacodegeeks.java8.parameter.names;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;

public class ParameterNames {
public static void main(String[] args) throws Exception {
Method method = ParameterNames.class.getMethod( "main", String[].class );
for( final Parameter parameter: method.getParameters() ) {
System.out.println( "Parameter: " + parameter.getName() );
}
}
}
</pre>

如果不使用–parameters參數(shù)來編譯這個類梧躺,然后運行這個類似谁,會得到下面的輸出:

<pre class="brush:shell; gutter: true; first-line: 1; highlight: []; html-script: false" style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 14px; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">Parameter: arg0
</pre>

如果使用–parameters參數(shù)來編譯這個類傲绣,程序的結(jié)構(gòu)會有所不同(參數(shù)的真實名字將會顯示出來):

<pre class="brush:shell; gutter: true; first-line: 1; highlight: []; html-script: false" style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 14px; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">Parameter: args
</pre>

對于有經(jīng)驗的Maven用戶掠哥,通過maven-compiler-plugin的配置可以將-parameters參數(shù)添加到編譯器中去巩踏。

<pre class="brush: xml; gutter: true; first-line: 1; highlight: []; html-script: false" style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 14px; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;"><plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<compilerArgument>-parameters</compilerArgument>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</pre>

針對Java 8最新發(fā)布的Eclipse Kepler SR2(請檢查這里的下載說明)提供了非常實用的配置選項,可以通過下圖的配置方式來控制編譯器行為

圖1. 配置Eclipse工程使之支持Java 8編譯器的新特性——parameters參數(shù)

此外续搀,Parameter類有一個很方便的方法isNamePresent()來驗證是否可以獲取參數(shù)的名字塞琼。

4. Java 類庫的新特性

Java 8 通過增加大量新類,擴展已有類的功能的方式來改善對并發(fā)編程禁舷、函數(shù)式編程彪杉、日期/時間相關(guān)操作以及其他更多方面的支持。

4.1 Optional

到目前為止牵咙,臭名昭著的空指針異常是導(dǎo)致Java應(yīng)用程序失敗的最常見原因派近。以前,為了解決空指針異常洁桌,Google公司著名的Guava項目引入了Optional類渴丸,Guava通過使用檢查空值的方式來防止代碼污染,它鼓勵程序員寫更干凈的代碼另凌。受到Google Guava的啟發(fā)谱轨,Optional類已經(jīng)成為Java 8類庫的一部分。

Optional實際上是個容器:它可以保存類型T的值吠谢,或者僅僅保存null土童。Optional提供很多有用的方法,這樣我們就不用顯式進行空值檢測工坊。更多詳情請參考官方文檔献汗。

我們下面用兩個小例子來演示如何使用Optional類:一個允許為空值,一個不允許為空值王污。

<pre class="brush: java; gutter: true; first-line: 1; highlight: []; html-script: false" style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 14px; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">Optional< String > fullName = Optional.ofNullable( null );
System.out.println( "Full Name is set? " + fullName.isPresent() );
System.out.println( "Full Name: " + fullName.orElseGet( () -> "[none]" ) );
System.out.println( fullName.map( s -> "Hey " + s + "!" ).orElse( "Hey Stranger!" ) );
</pre>

如果Optional類的實例為非空值的話罢吃,isPresent()返回true,否從返回false玉掸。為了防止Optional為空值刃麸,orElseGet()方法通過回調(diào)函數(shù)來產(chǎn)生一個默認值。map()函數(shù)對當(dāng)前Optional的值進行轉(zhuǎn)化司浪,然后返回一個新的Optional實例泊业。orElse()方法和orElseGet()方法類似,但是orElse接受一個默認值而不是一個回調(diào)函數(shù)啊易。下面是這個程序的輸出:

<pre class="brush:shell; gutter: true; first-line: 1; highlight: []; html-script: false" style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 14px; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">Full Name is set? false
Full Name: [none]
Hey Stranger!
</pre>

讓我們來看看另一個例子:

<pre class="brush: java; gutter: true; first-line: 1; highlight: []; html-script: false" style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 14px; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">Optional< String > firstName = Optional.of( "Tom" );
System.out.println( "First Name is set? " + firstName.isPresent() );
System.out.println( "First Name: " + firstName.orElseGet( () -> "[none]" ) );
System.out.println( firstName.map( s -> "Hey " + s + "!" ).orElse( "Hey Stranger!" ) );
System.out.println();
</pre>

下面是程序的輸出:

<pre class="brush:shell; gutter: true; first-line: 1; highlight: []; html-script: false" style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 14px; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">First Name is set? true
First Name: Tom
Hey Tom!
</pre>

更多詳情請參考官方文檔

4.2 Stream

最新添加的Stream API(java.util.stream) 把真正的函數(shù)式編程風(fēng)格引入到Java中吁伺。這是目前為止對Java類庫最好的補充,因為Stream API可以極大提供Java程序員的生產(chǎn)力租谈,讓程序員寫出高效率篮奄、干凈捆愁、簡潔的代碼。

Stream API極大簡化了集合框架的處理(但它的處理的范圍不僅僅限于集合框架的處理窟却,這點后面我們會看到)昼丑。讓我們以一個簡單的Task類為例進行介紹:

<pre class="brush: java; gutter: true; first-line: 1; highlight: []; html-script: false" style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 14px; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">public class Streams {
private enum Status {
OPEN, CLOSED
};

private static final class Task {
    private final Status status;
    private final Integer points;

    Task( final Status status, final Integer points ) {
        this.status = status;
        this.points = points;
    }

    public Integer getPoints() {
        return points;
    }

    public Status getStatus() {
        return status;
    }

    @Override
    public String toString() {
        return String.format( "[%s, %d]", status, points );
    }
}

}
</pre>

Task類有一個分數(shù)的概念(或者說是偽復(fù)雜度),其次是還有一個值可以為OPEN或CLOSED的狀態(tài).讓我們引入一個Task的小集合作為演示例子:

<pre class="brush: java; gutter: true; first-line: 1; highlight: []; html-script: false" style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 14px; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">final Collection< Task > tasks = Arrays.asList(
new Task( Status.OPEN, 5 ),
new Task( Status.OPEN, 13 ),
new Task( Status.CLOSED, 8 )
);
</pre>

我們下面要討論的第一個問題是所有狀態(tài)為OPEN的任務(wù)一共有多少分數(shù)夸赫?在Java 8以前菩帝,一般的解決方式用foreach循環(huán),但是在Java 8里面我們可以使用stream:一串支持連續(xù)茬腿、并行聚集操作的元素呼奢。

<pre class="brush: java; gutter: true; first-line: 1; highlight: []; html-script: false" style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 14px; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">// Calculate total points of all active tasks using sum()
final long totalPointsOfOpenTasks = tasks
.stream()
.filter( task -> task.getStatus() == Status.OPEN )
.mapToInt( Task::getPoints )
.sum();

System.out.println( "Total points: " + totalPointsOfOpenTasks );
</pre>

程序在控制臺上的輸出如下:

<pre class="brush:shell; gutter: true; first-line: 1; highlight: []; html-script: false" style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 14px; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">Total points: 18
</pre>

這里有幾個注意事項。第一切平,task集合被轉(zhuǎn)換化為其相應(yīng)的stream表示握础。然后,filter操作過濾掉狀態(tài)為CLOSED的task悴品。下一步禀综,mapToInt操作通過Task::getPoints這種方式調(diào)用每個task實例的getPoints方法把Task的stream轉(zhuǎn)化為Integer的stream。最后他匪,用sum函數(shù)把所有的分數(shù)加起來菇存,得到最終的結(jié)果。

在繼續(xù)講解下面的例子之前邦蜜,關(guān)于stream有一些需要注意的地方(詳情在這里).stream操作被分成了中間操作與最終操作這兩種依鸥。

中間操作返回一個新的stream對象。中間操作總是采用惰性求值方式悼沈,運行一個像filter這樣的中間操作實際上沒有進行任何過濾贱迟,相反它在遍歷元素時會產(chǎn)生了一個新的stream對象,這個新的stream對象包含原始stream
中符合給定謂詞的所有元素絮供。

像forEach衣吠、sum這樣的最終操作可能直接遍歷stream,產(chǎn)生一個結(jié)果或副作用壤靶。當(dāng)最終操作執(zhí)行結(jié)束之后缚俏,stream管道被認為已經(jīng)被消耗了,沒有可能再被使用了贮乳。在大多數(shù)情況下忧换,最終操作都是采用及早求值方式,及早完成底層數(shù)據(jù)源的遍歷向拆。

stream另一個有價值的地方是能夠原生支持并行處理亚茬。讓我們來看看這個算task分數(shù)和的例子。

<pre class="brush: java; gutter: true; first-line: 1; highlight: []; html-script: false" style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 14px; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">// Calculate total points of all tasks
final double totalPoints = tasks
.stream()
.parallel()
.map( task -> task.getPoints() ) // or map( Task::getPoints )
.reduce( 0, Integer::sum );

System.out.println( "Total points (all tasks): " + totalPoints );
</pre>

這個例子和第一個例子很相似浓恳,但這個例子的不同之處在于這個程序是并行運行的刹缝,其次使用reduce方法來算最終的結(jié)果碗暗。
下面是這個例子在控制臺的輸出:

<pre class="brush:shell; gutter: true; first-line: 1; highlight: []; html-script: false" style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 14px; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">Total points (all tasks): 26.0
</pre>

經(jīng)常會有這個一個需求:我們需要按照某種準(zhǔn)則來對集合中的元素進行分組。Stream也可以處理這樣的需求梢夯,下面是一個例子:

<pre class="brush: java; gutter: true; first-line: 1; highlight: []; html-script: false" style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 14px; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">// Group tasks by their status
final Map< Status, List< Task > > map = tasks
.stream()
.collect( Collectors.groupingBy( Task::getStatus ) );
System.out.println( map );
</pre>

這個例子的控制臺輸出如下:

<pre class="brush:shell; gutter: true; first-line: 1; highlight: []; html-script: false" style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 14px; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">{CLOSED=[[CLOSED, 8]], OPEN=[[OPEN, 5], [OPEN, 13]]}
</pre>

讓我們來計算整個集合中每個task分數(shù)(或權(quán)重)的平均值來結(jié)束task的例子言疗。

<pre class="brush: java; gutter: true; first-line: 1; highlight: []; html-script: false" style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 14px; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">// Calculate the weight of each tasks (as percent of total points)
final Collection< String > result = tasks
.stream() // Stream< String >
.mapToInt( Task::getPoints ) // IntStream
.asLongStream() // LongStream
.mapToDouble( points -> points / totalPoints ) // DoubleStream
.boxed() // Stream< Double >
.mapToLong( weigth -> ( long )( weigth * 100 ) ) // LongStream
.mapToObj( percentage -> percentage + "%" ) // Stream< String>
.collect( Collectors.toList() ); // List< String >

System.out.println( result );
</pre>

下面是這個例子的控制臺輸出:

<pre class="brush:shell; gutter: true; first-line: 1; highlight: []; html-script: false" style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 14px; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">[19%, 50%, 30%]
</pre>

最后,就像前面提到的厨疙,Stream API不僅僅處理Java集合框架洲守。像從文本文件中逐行讀取數(shù)據(jù)這樣典型的I/O操作也很適合用Stream API來處理疑务。下面用一個例子來應(yīng)證這一點沾凄。

<pre class="brush: java; gutter: true; first-line: 1; highlight: []; html-script: false" style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 14px; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">final Path path = new File( filename ).toPath();
try( Stream< String > lines = Files.lines( path, StandardCharsets.UTF_8 ) ) {
lines.onClose( () -> System.out.println("Done!") ).forEach( System.out::println );
}
</pre>

對一個stream對象調(diào)用onClose方法會返回一個在原有功能基礎(chǔ)上新增了關(guān)閉功能的stream對象,當(dāng)對stream對象調(diào)用close()方法時知允,與關(guān)閉相關(guān)的處理器就會執(zhí)行撒蟀。

Stream API、Lambda表達式方法引用接口默認方法與靜態(tài)方法的配合下是Java 8對現(xiàn)代軟件開發(fā)范式的回應(yīng)温鸽。更多詳情請參考官方文檔保屯。

4.3 Date/Time API (JSR 310)

Java 8通過發(fā)布新的Date-Time API (JSR 310)來進一步加強對日期與時間的處理。對日期與時間的操作一直是Java程序員最痛苦的地方之一涤垫。標(biāo)準(zhǔn)的 java.util.Date以及后來的java.util.Calendar一點沒有改善這種情況(可以這么說姑尺,它們一定程度上更加復(fù)雜)。

這種情況直接導(dǎo)致了Joda-Time——一個可替換標(biāo)準(zhǔn)日期/時間處理且功能非常強大的Java API的誕生蝠猬。Java 8新的Date-Time API (JSR 310)在很大程度上受到Joda-Time的影響切蟋,并且吸取了其精髓。新的java.time包涵蓋了所有處理日期榆芦,時間柄粹,日期/時間,時區(qū)匆绣,時刻(instants)驻右,過程(during)與時鐘(clock)的操作。在設(shè)計新版API時崎淳,十分注重與舊版API的兼容性:不允許有任何的改變(從java.util.Calendar中得到的深刻教訓(xùn))堪夭。如果需要修改,會返回這個類的一個新實例拣凹。

讓我們用例子來看一下新版API主要類的使用方法森爽。第一個是Clock類,它通過指定一個時區(qū)咐鹤,然后就可以獲取到當(dāng)前的時刻拗秘,日期與時間。Clock可以替換System.currentTimeMillis()與TimeZone.getDefault()祈惶。

<pre class="brush: java; gutter: true; first-line: 1; highlight: []; html-script: false" style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 14px; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">// Get the system clock as UTC offset
final Clock clock = Clock.systemUTC();
System.out.println( clock.instant() );
System.out.println( clock.millis() );
</pre>

下面是程序在控制臺上的輸出:

<pre class="brush:shell; gutter: true; first-line: 1; highlight: []; html-script: false" style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 14px; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">2014-04-12T15:19:29.282Z
1397315969360
</pre>

我們需要關(guān)注的其他類是LocaleDate與LocalTime雕旨。LocaleDate只持有ISO-8601格式且無時區(qū)信息的日期部分扮匠。相應(yīng)的,LocaleTime只持有ISO-8601格式且無時區(qū)信息的時間部分凡涩。LocaleDate與LocalTime都可以從Clock中得到棒搜。

<pre class="brush: java; gutter: true; first-line: 1; highlight: []; html-script: false" style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 14px; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">// Get the local date and local time
final LocalDate date = LocalDate.now();
final LocalDate dateFromClock = LocalDate.now( clock );

System.out.println( date );
System.out.println( dateFromClock );

// Get the local date and local time
final LocalTime time = LocalTime.now();
final LocalTime timeFromClock = LocalTime.now( clock );

System.out.println( time );
System.out.println( timeFromClock );
</pre>

下面是程序在控制臺上的輸出:

<pre class="brush:shell; gutter: true; first-line: 1; highlight: []; html-script: false" style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 14px; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">2014-04-12
2014-04-12
11:25:54.568
15:25:54.568
</pre>

LocaleDateTime把LocaleDate與LocaleTime的功能合并起來,它持有的是ISO-8601格式無時區(qū)信息的日期與時間活箕。下面是一個快速入門的例子力麸。

<pre class="brush: java; gutter: true; first-line: 1; highlight: []; html-script: false" style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 14px; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">// Get the local date/time
final LocalDateTime datetime = LocalDateTime.now();
final LocalDateTime datetimeFromClock = LocalDateTime.now( clock );

System.out.println( datetime );
System.out.println( datetimeFromClock );
</pre>

下面是程序在控制臺上的輸出:

<pre class="brush:shell; gutter: true; first-line: 1; highlight: []; html-script: false" style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 14px; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">2014-04-12T11:37:52.309
2014-04-12T15:37:52.309
</pre>

如果你需要特定時區(qū)的日期/時間,那么ZonedDateTime是你的選擇育韩。它持有ISO-8601格式具具有時區(qū)信息的日期與時間克蚂。下面是一些不同時區(qū)的例子:

<pre class="brush:shell; gutter: true; first-line: 1; highlight: []; html-script: false" style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 14px; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">// Get the zoned date/time
final ZonedDateTime zonedDatetime = ZonedDateTime.now();
final ZonedDateTime zonedDatetimeFromClock = ZonedDateTime.now( clock );
final ZonedDateTime zonedDatetimeFromZone = ZonedDateTime.now( ZoneId.of( "America/Los_Angeles" ) );

System.out.println( zonedDatetime );
System.out.println( zonedDatetimeFromClock );
System.out.println( zonedDatetimeFromZone );
</pre>

下面是程序在控制臺上的輸出:

<pre class="brush:shell; gutter: true; first-line: 1; highlight: []; html-script: false" style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 14px; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">2014-04-12T11:47:01.017-04:00[America/New_York]
2014-04-12T15:47:01.017Z
2014-04-12T08:47:01.017-07:00[America/Los_Angeles]
</pre>

最后,讓我們看一下Duration類:在秒與納秒級別上的一段時間筋讨。Duration使計算兩個日期間的不同變的十分簡單埃叭。下面讓我們看一個這方面的例子。

<pre class="brush: java; gutter: true; first-line: 1; highlight: []; html-script: false" style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 14px; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">// Get duration between two dates
final LocalDateTime from = LocalDateTime.of( 2014, Month.APRIL, 16, 0, 0, 0 );
final LocalDateTime to = LocalDateTime.of( 2015, Month.APRIL, 16, 23, 59, 59 );

final Duration duration = Duration.between( from, to );
System.out.println( "Duration in days: " + duration.toDays() );
System.out.println( "Duration in hours: " + duration.toHours() );
</pre>

上面的例子計算了兩個日期2014年4月16號與2014年4月16號之間的過程悉罕。下面是程序在控制臺上的輸出:

<pre class="brush:shell; gutter: true; first-line: 1; highlight: []; html-script: false" style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 14px; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">Duration in days: 365
Duration in hours: 8783
</pre>

對Java 8在日期/時間API的改進整體印象是非常非常好的赤屋。一部分原因是因為它建立在“久戰(zhàn)殺場”的Joda-Time基礎(chǔ)上,另一方面是因為用來大量的時間來設(shè)計它壁袄,并且這次程序員的聲音得到了認可类早。更多詳情請參考官方文檔

4.4 JavaScript引擎Nashorn

Nashorn嗜逻,一個新的JavaScript引擎隨著Java 8一起公諸于世涩僻,它允許在JVM上開發(fā)運行某些JavaScript應(yīng)用。Nashorn就是javax.script.ScriptEngine的另一種實現(xiàn)变泄,并且它們倆遵循相同的規(guī)則令哟,允許Java與JavaScript相互調(diào)用。下面看一個例子:

<pre class="brush: java; gutter: true; first-line: 1; highlight: []; html-script: false" style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 14px; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace;">ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName( "JavaScript" );

System.out.println( engine.getClass().getName() );
System.out.println( "Result:" + engine.eval( "function f() { return 1; }; f() + 1;" ) );
</pre>

下面是程序在控制臺上的輸出:

<pre class="brush:shell; gutter: true; first-line: 1; highlight: []; html-script: false" style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 14px; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace;">jdk.nashorn.api.scripting.NashornScriptEngine
Result: 2
</pre>

我們在后面的Java新工具章節(jié)會再次談到Nashorn妨蛹。

4.5 Base64

在Java 8中屏富,Base64編碼已經(jīng)成為Java類庫的標(biāo)準(zhǔn)。它的使用十分簡單蛙卤,下面讓我們看一個例子:

<pre class="brush: java; gutter: true; first-line: 1; highlight: []; html-script: false" style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 14px; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">package com.javacodegeeks.java8.base64;

import java.nio.charset.StandardCharsets;
import java.util.Base64;

public class Base64s {
public static void main(String[] args) {
final String text = "Base64 finally in Java 8!";

    final String encoded = Base64
        .getEncoder()
        .encodeToString( text.getBytes( StandardCharsets.UTF_8 ) );
    System.out.println( encoded );

    final String decoded = new String( 
        Base64.getDecoder().decode( encoded ),
        StandardCharsets.UTF_8 );
    System.out.println( decoded );
}

}
</pre>

程序在控制臺上輸出了編碼后的字符與解碼后的字符:

<pre class="brush:shell; gutter: true; first-line: 1; highlight: []; html-script: false" style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 14px; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">QmFzZTY0IGZpbmFsbHkgaW4gSmF2YSA4IQ==
Base64 finally in Java 8!
</pre>

Base64類同時還提供了對URL狠半、MIME友好的編碼器與解碼器(Base64.getUrlEncoder() / Base64.getUrlDecoder(), Base64.getMimeEncoder() / Base64.getMimeDecoder())。

4.6 并行(parallel)數(shù)組

Java 8增加了大量的新方法來對數(shù)組進行并行處理颤难∩衲辏可以說,最重要的是parallelSort()方法行嗤,因為它可以在多核機器上極大提高數(shù)組排序的速度已日。下面的例子展示了新方法(parallelXxx)的使用。

<pre class="brush: java; gutter: true; first-line: 1; highlight: []; html-script: false" style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 14px; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">package com.javacodegeeks.java8.parallel.arrays;

import java.util.Arrays;
import java.util.concurrent.ThreadLocalRandom;

public class ParallelArrays {
public static void main( String[] args ) {
long[] arrayOfLong = new long [ 20000 ];

    Arrays.parallelSetAll( arrayOfLong, 
        index -> ThreadLocalRandom.current().nextInt( 1000000 ) );
    Arrays.stream( arrayOfLong ).limit( 10 ).forEach( 
        i -> System.out.print( i + " " ) );
    System.out.println();

    Arrays.parallelSort( arrayOfLong );     
    Arrays.stream( arrayOfLong ).limit( 10 ).forEach( 
        i -> System.out.print( i + " " ) );
    System.out.println();
}

}
</pre>

上面的代碼片段使用了parallelSetAll()方法來對一個有20000個元素的數(shù)組進行隨機賦值栅屏。然后飘千,調(diào)用parallelSort方法堂鲜。這個程序首先打印出前10個元素的值,之后對整個數(shù)組排序护奈。這個程序在控制臺上的輸出如下(請注意數(shù)組元素是隨機生產(chǎn)的):

<pre class="brush:shell; gutter: true; first-line: 1; highlight: []; html-script: false" style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 14px; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">Unsorted: 591217 891976 443951 424479 766825 351964 242997 642839 119108 552378
Sorted: 39 220 263 268 325 607 655 678 723 793
</pre>

4.7 并發(fā)(Concurrency)

在新增Stream機制與lambda的基礎(chǔ)之上缔莲,在java.util.concurrent.ConcurrentHashMap中加入了一些新方法來支持聚集操作。同時也在java.util.concurrent.ForkJoinPool類中加入了一些新方法來支持共有資源池(common pool)(請查看我們關(guān)于Java 并發(fā)的免費課程)霉旗。

新增的java.util.concurrent.locks.StampedLock類提供一直基于容量的鎖痴奏,這種鎖有三個模型來控制讀寫操作(它被認為是不太有名的java.util.concurrent.locks.ReadWriteLock類的替代者)。

在java.util.concurrent.atomic包中還增加了下面這些類:

  • DoubleAccumulator
  • DoubleAdder
  • LongAccumulator
  • LongAdder

5. 新的Java工具

Java 8也帶來了一些新的命令行工具厌秒。在這節(jié)里我們將會介紹它們中最有趣的部分读拆。

5.1 Nashorn引擎: jjs

jjs是個基于Nashorn引擎的命令行工具。它接受一些JavaScript源代碼為參數(shù)简僧,并且執(zhí)行這些源代碼建椰。例如,我們創(chuàng)建一個具有如下內(nèi)容的func.js文件:

<pre class="brush: javascript; gutter: true; first-line: 1; highlight: []; html-script: false" style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 14px; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">function f() {
return 1;
};

print( f() + 1 );
</pre>

我們可以把這個文件作為參數(shù)傳遞給jjs使得這個文件可以在命令行中執(zhí)行:

https://blog.csdn.net/liubenlong007/article/details/62039628

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末岛马,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子屠列,更是在濱河造成了極大的恐慌啦逆,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,252評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件笛洛,死亡現(xiàn)場離奇詭異夏志,居然都是意外死亡,警方通過查閱死者的電腦和手機苛让,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,886評論 3 399
  • 文/潘曉璐 我一進店門沟蔑,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人狱杰,你說我怎么就攤上這事瘦材。” “怎么了仿畸?”我有些...
    開封第一講書人閱讀 168,814評論 0 361
  • 文/不壞的土叔 我叫張陵食棕,是天一觀的道長。 經(jīng)常有香客問我错沽,道長簿晓,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,869評論 1 299
  • 正文 為了忘掉前任千埃,我火速辦了婚禮憔儿,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘放可。我一直安慰自己谒臼,他們只是感情好唱逢,可當(dāng)我...
    茶點故事閱讀 68,888評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著屋休,像睡著了一般坞古。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上劫樟,一...
    開封第一講書人閱讀 52,475評論 1 312
  • 那天痪枫,我揣著相機與錄音,去河邊找鬼叠艳。 笑死奶陈,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的附较。 我是一名探鬼主播吃粒,決...
    沈念sama閱讀 41,010評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼拒课!你這毒婦竟也來了徐勃?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,924評論 0 277
  • 序言:老撾萬榮一對情侶失蹤早像,失蹤者是張志新(化名)和其女友劉穎僻肖,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體卢鹦,經(jīng)...
    沈念sama閱讀 46,469評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡臀脏,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,552評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了冀自。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片揉稚。...
    茶點故事閱讀 40,680評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖熬粗,靈堂內(nèi)的尸體忽然破棺而出搀玖,到底是詐尸還是另有隱情,我是刑警寧澤荐糜,帶...
    沈念sama閱讀 36,362評論 5 351
  • 正文 年R本政府宣布巷怜,位于F島的核電站,受9級特大地震影響暴氏,放射性物質(zhì)發(fā)生泄漏延塑。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,037評論 3 335
  • 文/蒙蒙 一答渔、第九天 我趴在偏房一處隱蔽的房頂上張望关带。 院中可真熱鬧,春花似錦、人聲如沸宋雏。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,519評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽磨总。三九已至嗦明,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間蚪燕,已是汗流浹背娶牌。 一陣腳步聲響...
    開封第一講書人閱讀 33,621評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留馆纳,地道東北人诗良。 一個月前我還...
    沈念sama閱讀 49,099評論 3 378
  • 正文 我出身青樓,卻偏偏與公主長得像鲁驶,于是被迫代替她去往敵國和親鉴裹。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,691評論 2 361