java8新特性介紹

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

  1. 介紹

  2. Java語言的新特性

    2.1 Lambdas表達(dá)式與Functional接口

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

    2.3 方法引用

    2.4 重復(fù)注解

    2.5 更好的類型推測(cè)機(jī)制

    2.6 擴(kuò)展注解的支持

  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虛擬機(jī)(JVM)的新特性

  7. 總結(jié)

  8. 更多資源

1.介紹

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

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

  • Java語言
  • 編譯器
  • 類庫
  • 工具
  • Java運(yùn)行時(shí)(JVM)

2.Java語言的新特性

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

2.1 Lambda表達(dá)式與Functional接口

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

關(guān)于Lambda設(shè)計(jì)的討論占用了大量的時(shí)間與社區(qū)的努力??上驳氖牵罱K找到了一個(gè)平衡點(diǎn),使得可以使用一種即簡(jiǎn)潔又緊湊的新方式來構(gòu)造Lambdas。在最簡(jiǎn)單的形式中,一個(gè)lambda可以由用逗號(hào)分隔的參數(shù)列表、–>符號(hào)與函數(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>

請(qǐng)注意參數(shù)e的類型是由編譯器推測(cè)出來的。同時(shí),你也可以通過把參數(shù)類型與參數(shù)包括在括號(hào)中的形式直接給出參數(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ù)體會(huì)更加復(fù)雜,這時(shí)可以把函數(shù)體放到在一對(duì)花括號(hào)中,就像在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的話,它們會(huì)被隱含的轉(zhuǎn)為final,這樣效率更高)。例如,下面兩個(gè)代碼片段是等價(jià)的:

<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可能會(huì)返回一個(gè)值。返回值的類型也是由編譯器推測(cè)出來的。如果lambda的函數(shù)體只有一行的話,那么沒有必要顯式使用return語句。下面兩個(gè)代碼片段是等價(jià)的:

<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è)計(jì)者投入了大量精力來思考如何使現(xiàn)有的函數(shù)友好地支持lambda。最終采取的方法是:增加函數(shù)式接口的概念。函數(shù)式接口就是一個(gè)具有一個(gè)方法的普通接口。像這樣的接口,可以被隱式轉(zhuǎn)換為lambda表達(dá)式。java.lang.Runnable與java.util.concurrent.Callable是函數(shù)式接口最典型的兩個(gè)例子。在實(shí)際使用過程中,函數(shù)式接口是容易出錯(cuò)的:如有某個(gè)人在接口定義中增加了另一個(gè)方法,這時(shí),這個(gè)接口就不再是函數(shù)式的了,并且編譯過程也會(huì)失敗。為了克服函數(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>

需要記住的一件事是:默認(rèn)方法與靜態(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最大的賣點(diǎn)。它具有吸引越來越多程序員到Java平臺(tái)上的潛力,并且能夠在純Java語言環(huán)境中提供一種優(yōu)雅的方式來支持函數(shù)式編程。更多詳情可以參考官方文檔。

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

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

默認(rèn)方法與抽象方法不同之處在于抽象方法必須要求實(shí)現(xiàn),但是默認(rèn)方法則沒有這個(gè)要求。相反,每個(gè)接口都必須提供一個(gè)所謂的默認(rèn)實(shí)現(xiàn),這樣所有的接口實(shí)現(xiàn)者將會(huì)默認(rèn)繼承它(如果有必要的話,可以覆蓋這個(gè)默認(rèn)實(shí)現(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聲明了一個(gè)默認(rèn)方法notRequired(),Defaulable接口的實(shí)現(xiàn)者之一DefaultableImpl實(shí)現(xiàn)了這個(gè)接口,并且讓默認(rèn)方法保持原樣。Defaulable接口的另一個(gè)實(shí)現(xiàn)者OverridableImpl用自己的方法覆蓋了默認(rèn)方法。

Java 8帶來的另一個(gè)有趣的特性是接口可以聲明(并且可以提供實(shí)現(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>

下面的一小段代碼片段把上面的默認(rè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;">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>

這個(gè)程序的控制臺(tái)輸出如下:

<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中,默認(rèn)方法的實(shí)現(xiàn)是非常高效的,并且通過字節(jié)碼指令為方法調(diào)用提供了支持。默認(rèn)方法允許繼續(xù)使用現(xiàn)有的Java接口,而同時(shí)能夠保障正常的編譯過程。這方面好的例子是大量的方法被添加到j(luò)ava.util.Collection接口中去:stream(),parallelStream(),forEach(),removeIf(),……

盡管默認(rèn)方法非常強(qiáng)大,但是在使用默認(rèn)方法時(shí)我們需要小心注意一個(gè)地方:在聲明一個(gè)默認(rèn)方法前,請(qǐng)仔細(xì)思考是不是真的有必要使用默認(rèn)方法,因?yàn)槟J(rèn)方法會(huì)帶給程序歧義,并且在復(fù)雜的繼承體系中容易產(chǎn)生編譯錯(cuò)誤。更多詳情請(qǐng)參考官方文檔

2.3 方法引用

方法引用提供了非常有用的語法,可以直接引用已有Java類或?qū)ο螅▽?shí)例)的方法或構(gòu)造器。與lambda聯(lián)合使用,方法引用可以使語言的構(gòu)造更緊湊簡(jiǎn)潔,減少冗余代碼。

下面,我們以定義了4個(gè)方法的Car這個(gè)類作為例子,區(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。請(qǐng)注意構(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。請(qǐng)注意這個(gè)方法接受一個(gè)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>

第三種方法引用是特定類的任意對(duì)象的方法引用,它的語法是Class::method。請(qǐng)注意,這個(gè)方法沒有參數(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>

最后,第四種方法引用是特定對(duì)象的方法引用,它的語法是instance::method。請(qǐng)注意,這個(gè)方法接受一個(gè)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>

運(yùn)行上面的Java程序在控制臺(tái)上會(huì)有下面的輸出(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;">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)于方法引用的更多詳情請(qǐng)參考官方文檔。

2.4 重復(fù)注解

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

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

<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>

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

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

程序輸出結(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>

更多詳情請(qǐng)參考官方文檔

2.5 更好的類型推測(cè)機(jī)制

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

<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ù)類型可以被推測(cè)出,所以就不必明確給出。在Java 7中,相同的例子將不會(huì)通過編譯,正確的書寫方式是 Value.< String >defaultValue()。

2.6 擴(kuò)展注解的支持

Java 8擴(kuò)展了注解的上下文?,F(xiàn)在幾乎可以為任何東西添加注解:局部變量、泛型類、父類與接口的實(shí)現(xiàn),就連方法的異常也能添加注解。下面演示幾個(gè)例子:

<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是兩個(gè)新添加的用于描述適當(dāng)?shù)淖⒔馍舷挛牡脑仡愋?。在Java語言中,注解處理API也有小的改動(dòng)來識(shí)別新增的類型注解。

3. Java編譯器的新特性

3.1 參數(shù)名字

很長一段時(shí)間里,Java程序員一直在發(fā)明不同的方式使得方法參數(shù)的名字能保留在Java字節(jié)碼中,并且能夠在運(yùn)行時(shí)獲取它們(比如,Paranamer類庫)。最終,在Java 8中把這個(gè)強(qiáng)烈要求的功能添加到語言層面(通過反射API與Parameter.getName()方法)與字節(jié)碼文件(通過新版的javac的–parameters選項(xià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;">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ù)來編譯這個(gè)類,然后運(yùn)行這個(gè)類,會(huì)得到下面的輸出:

<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ù)來編譯這個(gè)類,程序的結(jié)構(gòu)會(huì)有所不同(參數(shù)的真實(shí)名字將會(huì)顯示出來):

<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>

對(duì)于有經(jīng)驗(yàn)的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>

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

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

此外,Parameter類有一個(gè)很方便的方法isNamePresent()來驗(yàn)證是否可以獲取參數(shù)的名字。

4. Java 類庫的新特性

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

4.1 Optional

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

Optional實(shí)際上是個(gè)容器:它可以保存類型T的值,或者僅僅保存null。Optional提供很多有用的方法,這樣我們就不用顯式進(jìn)行空值檢測(cè)。更多詳情請(qǐng)參考官方文檔。

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

<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類的實(shí)例為非空值的話,isPresent()返回true,否從返回false。為了防止Optional為空值,orElseGet()方法通過回調(diào)函數(shù)來產(chǎn)生一個(gè)默認(rèn)值。map()函數(shù)對(duì)當(dāng)前Optional的值進(jìn)行轉(zhuǎn)化,然后返回一個(gè)新的Optional實(shí)例。orElse()方法和orElseGet()方法類似,但是orElse接受一個(gè)默認(rèn)值而不是一個(gè)回調(diào)函數(shù)。下面是這個(gè)程序的輸出:

<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>

讓我們來看看另一個(gè)例子:

<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>

更多詳情請(qǐng)參考官方文檔

4.2 Stream

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

Stream API極大簡(jiǎn)化了集合框架的處理(但它的處理的范圍不僅僅限于集合框架的處理,這點(diǎn)后面我們會(huì)看到)。讓我們以一個(gè)簡(jiǎn)單的Task類為例進(jì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;">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類有一個(gè)分?jǐn)?shù)的概念(或者說是偽復(fù)雜度),其次是還有一個(gè)值可以為OPEN或CLOSED的狀態(tài).讓我們引入一個(gè)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>

我們下面要討論的第一個(gè)問題是所有狀態(tài)為OPEN的任務(wù)一共有多少分?jǐn)?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>

程序在控制臺(tái)上的輸出如下:

<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>

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

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

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

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

stream另一個(gè)有價(jià)值的地方是能夠原生支持并行處理。讓我們來看看這個(gè)算task分?jǐn)?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>

這個(gè)例子和第一個(gè)例子很相似,但這個(gè)例子的不同之處在于這個(gè)程序是并行運(yùn)行的,其次使用reduce方法來算最終的結(jié)果。
下面是這個(gè)例子在控制臺(tái)的輸出:

<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)常會(huì)有這個(gè)一個(gè)需求:我們需要按照某種準(zhǔn)則來對(duì)集合中的元素進(jìn)行分組。Stream也可以處理這樣的需求,下面是一個(gè)例子:

<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>

這個(gè)例子的控制臺(tái)輸出如下:

<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>

讓我們來計(jì)算整個(gè)集合中每個(gè)task分?jǐn)?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>

下面是這個(gè)例子的控制臺(tái)輸出:

<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來處理。下面用一個(gè)例子來應(yīng)證這一點(diǎ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;">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>

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

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

4.3 Date/Time API (JSR 310)

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

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

讓我們用例子來看一下新版API主要類的使用方法。第一個(gè)是Clock類,它通過指定一個(gè)時(shí)區(qū),然后就可以獲取到當(dāng)前的時(shí)刻,日期與時(shí)間。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>

下面是程序在控制臺(tái)上的輸出:

<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格式且無時(shí)區(qū)信息的日期部分。相應(yīng)的,LocaleTime只持有ISO-8601格式且無時(shí)區(qū)信息的時(shí)間部分。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>

下面是程序在控制臺(tái)上的輸出:

<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格式無時(shí)區(qū)信息的日期與時(shí)間。下面是一個(gè)快速入門的例子。

<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>

下面是程序在控制臺(tái)上的輸出:

<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>

如果你需要特定時(shí)區(qū)的日期/時(shí)間,那么ZonedDateTime是你的選擇。它持有ISO-8601格式具具有時(shí)區(qū)信息的日期與時(shí)間。下面是一些不同時(shí)區(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>

下面是程序在控制臺(tái)上的輸出:

<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類:在秒與納秒級(jí)別上的一段時(shí)間。Duration使計(jì)算兩個(gè)日期間的不同變的十分簡(jiǎn)單。下面讓我們看一個(gè)這方面的例子。

<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>

上面的例子計(jì)算了兩個(gè)日期2014年4月16號(hào)與2014年4月16號(hào)之間的過程。下面是程序在控制臺(tái)上的輸出:

<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>

對(duì)Java 8在日期/時(shí)間API的改進(jìn)整體印象是非常非常好的。一部分原因是因?yàn)樗⒃凇熬脩?zhàn)殺場(chǎng)”的Joda-Time基礎(chǔ)上,另一方面是因?yàn)橛脕泶罅康臅r(shí)間來設(shè)計(jì)它,并且這次程序員的聲音得到了認(rèn)可。更多詳情請(qǐng)參考官方文檔。

4.4 JavaScript引擎Nashorn

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

<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>

下面是程序在控制臺(tái)上的輸出:

<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>

我們?cè)诤竺娴?a target="_blank" rel="nofollow">Java新工具章節(jié)會(huì)再次談到Nashorn。

4.5 Base64

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

<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>

程序在控制臺(tái)上輸出了編碼后的字符與解碼后的字符:

<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類同時(shí)還提供了對(duì)URL、MIME友好的編碼器與解碼器(Base64.getUrlEncoder() / Base64.getUrlDecoder(), Base64.getMimeEncoder() / Base64.getMimeDecoder())。

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

Java 8增加了大量的新方法來對(duì)數(shù)組進(jìn)行并行處理??梢哉f,最重要的是parallelSort()方法,因?yàn)樗梢栽诙嗪藱C(jī)器上極大提高數(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()方法來對(duì)一個(gè)有20000個(gè)元素的數(shù)組進(jìn)行隨機(jī)賦值。然后,調(diào)用parallelSort方法。這個(gè)程序首先打印出前10個(gè)元素的值,之后對(duì)整個(gè)數(shù)組排序。這個(gè)程序在控制臺(tái)上的輸出如下(請(qǐng)注意數(shù)組元素是隨機(jī)生產(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機(jī)制與lambda的基礎(chǔ)之上,在java.util.concurrent.ConcurrentHashMap中加入了一些新方法來支持聚集操作。同時(shí)也在java.util.concurrent.ForkJoinPool類中加入了一些新方法來支持共有資源池(common pool)(請(qǐng)查看我們關(guān)于Java 并發(fā)的免費(fèi)課程)。

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

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

  • DoubleAccumulator
  • DoubleAdder
  • LongAccumulator
  • LongAdder

5. 新的Java工具

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

5.1 Nashorn引擎: jjs

jjs是個(gè)基于Nashorn引擎的命令行工具。它接受一些JavaScript源代碼為參數(shù),并且執(zhí)行這些源代碼。例如,我們創(chuàng)建一個(gè)具有如下內(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>

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

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容