Java8新特性

Java 8自Java 5(發(fā)行于2004)以來最具革命性的版本长窄。Java 8 為Java語言滔吠、編譯器、類庫挠日、開發(fā)工具與JVM(Java虛擬機(jī))帶來了大量新特性疮绷。

分別涉及到Java平臺(tái)某一特定方面的內(nèi)容:

Java語言

編譯器

類庫

工具

Java運(yùn)行時(shí)(JVM)

1.Java語言的新特性

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

1.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ū)的努力驾胆』猎瑁可喜的是,最終找到了一個(gè)平衡點(diǎn)丧诺,使得可以使用一種即簡潔又緊湊的新方式來構(gòu)造Lambdas入桂。在最簡單的形式中,一個(gè)lambda可以由用逗號(hào)分隔的參數(shù)列表驳阎、–>符號(hào)與函數(shù)體三部分表示抗愁。例如:

Arrays.asList("a","b","d").forEach( e -> System.out.println( e ) );

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

Arrays.asList("a","b","d").forEach( ( String e ) -> System.out.println( e ) );

在某些情況下lambda的函數(shù)體會(huì)更加復(fù)雜蜘腌,這時(shí)可以把函數(shù)體放到在一對(duì)花括號(hào)中,就像在Java中定義普通函數(shù)一樣饵隙。例如:

Arrays.asList("a","b","d").forEach( e -> {

System.out.print( e );

System.out.print( e );

} );

Lambda可以引用類的成員變量與局部變量(如果這些變量不是final的話撮珠,它們會(huì)被隱含的轉(zhuǎn)為final,這樣效率更高)金矛。例如芯急,下面兩個(gè)代碼片段是等價(jià)的:

String separator =",";

Arrays.asList("a","b","d").forEach(

( String e ) -> System.out.print( e + separator ) );

和:

final String separator =",";

Arrays.asList("a","b","d").forEach(

( String e ) -> System.out.print( e + separator ) );

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

Arrays.asList("a","b","d").sort( ( e1, e2 ) -> e1.compareTo( e2 ) );

和:

Arrays.asList("a","b","d").sort( ( e1, e2 ) -> {

int ?result = e1.compareTo( e2 );

return result;

} );

語言設(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ù)式接口的定義:

@FunctionalInterface

public interface Functional {

void method();

}

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

@FunctionalInterface

public interface FunctionalDefaultMethods {

void ?method();

default void defaultMethod() {

? ? ?}

}

Lambda是Java 8最大的賣點(diǎn)乔妈。它具有吸引越來越多程序員到Java平臺(tái)上的潛力,并且能夠在純Java語言環(huán)境中提供一種優(yōu)雅的方式來支持函數(shù)式編程氓皱。

1.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))搞监。讓我們看看下面的例子:

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

? ? }

?}

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)方法秤标。例如:

private interface DefaulableFactory {

// Interfaces now allow static methods

static Defaulable create( Supplier< Defaulable > supplier ) {

return supplier.get();

?}

}

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

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() );

}

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

Default implementation

Overridden implementation

在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ò)誤。

2.3 方法引用

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

下面,我們以定義了4個(gè)方法的Car這個(gè)類作為例子神郊,區(qū)分Java中支持的4種不同的方法引用葵腹。

public static class Car {

public static Car create(finalSupplier< 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() );

? }

}

第一種方法引用是構(gòu)造器引用,它的語法是Class::new屿岂,或者更一般的Class< T >::new。請(qǐng)注意構(gòu)造器沒有參數(shù)鲸匿。

final Car car = Car.create( Car::new);

final List< Car > cars = Arrays.asList( car );

第二種方法引用是靜態(tài)方法引用爷怀,它的語法是Class::static_method。請(qǐng)注意這個(gè)方法接受一個(gè)Car類型的參數(shù)带欢。

cars.forEach( Car::collide );

第三種方法引用是特定類的任意對(duì)象的方法引用运授,它的語法是Class::method。請(qǐng)注意乔煞,這個(gè)方法沒有參數(shù)吁朦。

cars.forEach( Car::repair );

最后,第四種方法引用是特定對(duì)象的方法引用渡贾,它的語法是instance::method逗宜。請(qǐng)注意,這個(gè)方法接受一個(gè)Car類型的參數(shù)

final Car police = Car.create( Car::new);

cars.forEach( police::follow );

運(yùn)行上面的Java程序在控制臺(tái)上會(huì)有下面的輸出(Car的實(shí)例可能不一樣):

Collided com.javacodegeeks.java8.method.references.MethodReferences$Car@7a81197d

Repaired com.javacodegeeks.java8.method.references.MethodReferences$Car@7a81197d

Following the com.javacodegeeks.java8.method.references.MethodReferences$Car@7a81197d

關(guān)于方法引用的更多詳情請(qǐng)參考官方文檔空骚。

2.4 重復(fù)注解

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

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

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@interfaceFilter {

? ? String value();

? ? ? ?};

@Filter("filter1")

@Filter("filter2")

publicinterfaceFilterable {

? ?}

public static void main(String[] args) {

for( Filter filter: Filterable.class.getAnnotationsByType( Filter.class) ) {

? ?System.out.println( filter.value() );

? ? ?}

? ? }

? }

正如我們看到的角钩,這里有個(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é)果如下:

filter1

filter2

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

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

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

packagecom.javacodegeeks.java8.type.inference;

publicclassValue< T > {

publicstatic< T > T defaultValue() {

? ? returnnull;

? ?}

publicT getOrDefault( T value, T defaultValue ) {

return( value !=null) ? value : defaultValue;

? ? }

?}

這里是Value< String >類型的用法依疼。

package com.javacodegeeks.java8.type.inference;

public class TypeInference {

public static void main(String[] args) {

final Value< String > value =newValue<>();

value.getOrDefault("22", Value.defaultValue() );

? ?} .?

}

Value.defaultValue()的參數(shù)類型可以被推測出,所以就不必明確給出而芥。在Java 7中律罢,相同的例子將不會(huì)通過編譯,正確的書寫方式是 Value.< String >defaultValue()棍丐。

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

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

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@interfaceNonEmpty {

? ?}

public static class Holder<@NonEmptyT >extends@NonEmptyObject {

public void method()throws@NonEmptyException {

? ? }

? ?}

@SuppressWarnings("unused")

public static void main(String[] args) {

final Holder< String > holder =new@NonEmptyHolder< String >();

@NonEmptyCollection<@NonEmptyString > strings =newArrayList<>();

? ? }

? }

ElementType.TYPE_USE和ElementType.TYPE_PARAMETER是兩個(gè)新添加的用于描述適當(dāng)?shù)淖⒔馍舷挛牡脑仡愋兔匕浮T贘ava語言中砰苍,注解處理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))中。

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)throwsException {

Method method = ParameterNames.class.getMethod("main", String[].class);

for(final Parameter parameter: method.getParameters() ) {

System.out.println("Parameter: "+ parameter.getName() );

? }

? }

}

如果不使用–parameters參數(shù)來編譯這個(gè)類荐捻,然后運(yùn)行這個(gè)類黍少,會(huì)得到下面的輸出:

Parameter: arg0

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

Parameter: args

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

org.apache.maven.plugins

maven-compiler-plugin

3.1

-parameters

1.8

1.8

針對(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)行空值檢測。更多詳情請(qǐng)參考官方文檔亡驰。

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

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!") );

如果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è)程序的輸出:

Full Name isset?false

Full Name: [none]

Hey Stranger!

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

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();

下面是程序的輸出:

First Name isset?true

First Name: Tom

Hey Tom!

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

4.2 Stream

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

Stream API極大簡化了集合框架的處理(但它的處理的范圍不僅僅限于集合框架的處理采缚,這點(diǎn)后面我們會(huì)看到)针炉。讓我們以一個(gè)簡單的Task類為例進(jìn)行介紹:

public class Streams? {

private enum Status {

OPEN, CLOSED

? };

private static final classTask {

private final Status status;

private final Integer points;

Task(finalStatus status,finalInteger 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 );

? ?}

? ?}

}

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

final Collection< Task > tasks = Arrays.asList(

new Task( Status.OPEN,5),

new Task( Status.OPEN,13),

new Task( Status.CLOSED,8)

);

我們下面要討論的第一個(gè)問題是所有狀態(tài)為OPEN的任務(wù)一共有多少分?jǐn)?shù)扳抽?在Java 8以前篡帕,一般的解決方式用foreach循環(huán),但是在Java 8里面我們可以使用stream:一串支持連續(xù)贸呢、并行聚集操作的元素镰烧。

// 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 );

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

Total points: 18

這里有幾個(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ù)和的例子。

// Calculate total points of all tasks

finaldoubletotalPoints = tasks

.stream()

.parallel()

.map( task -> task.getPoints() )// or map( Task::getPoints )

.reduce(0, Integer::sum );

System.out.println("Total points (all tasks): "+ totalPoints );

這個(gè)例子和第一個(gè)例子很相似始腾,但這個(gè)例子的不同之處在于這個(gè)程序是并行運(yùn)行的旱爆,其次使用reduce方法來算最終的結(jié)果舀射。

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

Total points (all tasks): 26.0

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

// Group tasks by their status

finalMap< Status, List< Task > > map = tasks

.stream()

.collect( Collectors.groupingBy( Task::getStatus ) );

System.out.println( map );

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

{CLOSED=[[CLOSED, 8]], OPEN=[[OPEN, 5], [OPEN, 13]]}

讓我們來計(jì)算整個(gè)集合中每個(gè)task分?jǐn)?shù)(或權(quán)重)的平均值來結(jié)束task的例子脆烟。

// 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 );

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

[19%, 50%, 30%]

最后,就像前面提到的房待,Stream API不僅僅處理Java集合框架邢羔。像從文本文件中逐行讀取數(shù)據(jù)這樣典型的I/O操作也很適合用Stream API來處理。下面用一個(gè)例子來應(yīng)證這一點(diǎn)桑孩。

finalPath path =newFile( filename ).toPath();

try( Stream< String > lines = Files.lines( path, StandardCharsets.UTF_8 ) ) {

lines.onClose( () -> System.out.println("Done!") ).forEach( System.out::println );

}

對(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()。

// Get the system clock as UTC offset

finalClock clock = Clock.systemUTC();

System.out.println( clock.instant() );

System.out.println( clock.millis() );

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

2014-04-12T15:19:29.282Z

1397315969360

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

// Get the local date and local time

finalLocalDate date = LocalDate.now();

finalLocalDate dateFromClock = LocalDate.now( clock );

System.out.println( date );

System.out.println( dateFromClock );

// Get the local date and local time

finalLocalTime time = LocalTime.now();

finalLocalTime timeFromClock = LocalTime.now( clock );

System.out.println( time );

System.out.println( timeFromClock );

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

2014-04-12

2014-04-12

11:25:54.568

15:25:54.568

LocaleDateTime把LocaleDate與LocaleTime的功能合并起來齐邦,它持有的是ISO-8601格式無時(shí)區(qū)信息的日期與時(shí)間。下面是一個(gè)快速入門的例子稚新。

// Get the local date/time

finalLocalDateTime datetime = LocalDateTime.now();

finalLocalDateTime datetimeFromClock = LocalDateTime.now( clock );

System.out.println( datetime );

System.out.println( datetimeFromClock );

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

2014-04-12T11:37:52.309

2014-04-12T15:37:52.309

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

//Get the zoneddate/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 );

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

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]

最后戒突,讓我們看一下Duration類:在秒與納秒級(jí)別上的一段時(shí)間铲掐。Duration使計(jì)算兩個(gè)日期間的不同變的十分簡單婉支。下面讓我們看一個(gè)這方面的例子。

// Get duration between two dates

finalLocalDateTime from = LocalDateTime.of(2014, Month.APRIL,16,0,0,0);

finalLocalDateTime to = LocalDateTime.of(2015, Month.APRIL,16,23,59,59);

finalDuration duration = Duration.between( from, to );

System.out.println("Duration in days: "+ duration.toDays() );

System.out.println("Duration in hours: "+ duration.toHours() );

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

Durationindays: 365

Durationinhours: 8783

對(duì)Java 8在日期/時(shí)間API的改進(jìn)整體印象是非常非常好的向挖。一部分原因是因?yàn)樗⒃凇熬脩?zhàn)殺場”的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è)例子:

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;") );

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

jdk.nashorn.api.scripting.NashornScriptEngine

Result: 2

我們?cè)诤竺娴?/a>Java新工具章節(jié)會(huì)再次談到Nashorn舞箍。

4.5 Base64

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

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 =newString(

Base64.getDecoder().decode( encoded ),

StandardCharsets.UTF_8 );

System.out.println( decoded );

? ?}

}

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

QmFzZTY0IGZpbmFsbHkgaW4gSmF2YSA4IQ==

Base64 finallyinJava 8!

Base64類同時(shí)還提供了對(duì)URL占拍、MIME友好的編碼器與解碼器(Base64.getUrlEncoder() / Base64.getUrlDecoder(), Base64.getMimeEncoder() / Base64.getMimeDecoder())略就。

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

Java 8增加了大量的新方法來對(duì)數(shù)組進(jìn)行并行處理』尉疲可以說表牢,最重要的是parallelSort()方法,因?yàn)樗梢栽诙嗪藱C(jī)器上極大提高數(shù)組排序的速度贝次。下面的例子展示了新方法(parallelXxx)的使用崔兴。

packagecom.javacodegeeks.java8.parallel.arrays;

importjava.util.Arrays;

importjava.util.concurrent.ThreadLocalRandom;

publicclassParallelArrays {

publicstaticvoidmain( String[] args ) {

long[] arrayOfLong =newlong[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();

? ?}

}

上面的代碼片段使用了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)的):

Unsorted: 591217 891976 443951 424479 766825 351964 242997 642839 119108 552378

Sorted: 39 220 263 268 325 607 655 678 723 793

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文件:

function f() {

return1;

? ? };

print( f() + 1 );

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

jjs func.js

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

1

2

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

5.2 類依賴分析器jdeps

jdeps是一個(gè)很有用的命令行工具等龙。它可以顯示Java類的包級(jí)別或類級(jí)別的依賴处渣。它接受一個(gè).class文件,一個(gè)目錄蛛砰,或者一個(gè)jar文件作為輸入罐栈。jdeps默認(rèn)把結(jié)果輸出到系統(tǒng)輸出(控制臺(tái))上。

下面我們查看現(xiàn)階段較流行的Spring框架類庫的依賴報(bào)告泥畅,為了簡化這個(gè)例子荠诬,我們只分析一個(gè)jar文件:org.springframework.core-3.0.5.RELEASE.jar

jdeps org.springframework.core-3.0.5.RELEASE.jar

這個(gè)命令輸出的內(nèi)容很多,所以這里我們只選取一小部分位仁。依賴信息按照包名進(jìn)行分組柑贞。如果依賴不在classpath中,那么就會(huì)顯示not found聂抢。

org.springframework.core-3.0.5.RELEASE.jar -> C:\Program Files\Java\jdk1.8.0\jre\lib\rt.jar

org.springframework.core (org.springframework.core-3.0.5.RELEASE.jar)

? ? ? ?-> java.io

? ? ? ?-> java.lang

? ? ? ?-> java.lang.annotation

? ? ? ?-> java.lang.ref

? ? ? ? -> java.lang.reflect

? ? ? ? -> java.util

? ? ? ? ? -> java.util.concurrent

? ? ? ? ? -> org.apache.commons.logging???????????????????????? not found

? ? ? ? ? ?-> org.springframework.asm??????????????????????????? not found

? ? ? ? ? -> org.springframework.asm.commons??????????????????? not found

org.springframework.core.annotation (org.springframework.core-3.0.5.RELEASE.jar)

? ? ? ? ? -> java.lang

? ? ? ? ? -> java.lang.annotation

? ? ? ? ?-> java.lang.reflect

? ? ? ? ? -> java.util

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

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

PermGen空間被移除了钧嘶,取而代之的是MetaspaceJEP 122)。JVM選項(xiàng)-XX:PermSize-XX:MaxPermSize分別被-XX:MetaSpaceSize-XX:MaxMetaspaceSize所代替琳疏。

7. 總結(jié)

更多展望:Java 8通過發(fā)布一些可以增加程序員生產(chǎn)力的特性來推進(jìn)這個(gè)偉大的平臺(tái)的進(jìn)步∮芯觯現(xiàn)在把生產(chǎn)環(huán)境遷移到Java 8還為時(shí)尚早闸拿,但是在接下來的幾個(gè)月里,它會(huì)被大眾慢慢的接受书幕。毫無疑問新荤,現(xiàn)在是時(shí)候讓你的代碼與Java 8兼容,并且在Java 8足夠安全穩(wěn)定的時(shí)候遷移到Java 8按咒。

java8,四大內(nèi)置核心函數(shù)式接口

Consumer:消費(fèi)型接口

? ? ? ? ? ? ? void accept(T t);

Supplier: 供給型接口

? ? ? ? ? ? ? ?T ?get();

Function: 函數(shù)型接口

? ? ? ? ? ? ? ? R apply(T t);

Predicate: 斷言型接口

? ? ? ? ? ? ? ?boolean test(T t);

作者:誰在烽煙彼岸

鏈接:http://www.reibang.com/p/77e91e0c8b21

來源:簡書

著作權(quán)歸作者所有但骨。商業(yè)轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán)励七,非商業(yè)轉(zhuǎn)載請(qǐng)注明出處。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末奔缠,一起剝皮案震驚了整個(gè)濱河市掠抬,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌校哎,老刑警劉巖两波,帶你破解...
    沈念sama閱讀 216,402評(píng)論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異闷哆,居然都是意外死亡腰奋,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門抱怔,熙熙樓的掌柜王于貴愁眉苦臉地迎上來劣坊,“玉大人,你說我怎么就攤上這事屈留【直” “怎么了?”我有些...
    開封第一講書人閱讀 162,483評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵灌危,是天一觀的道長康二。 經(jīng)常有香客問我,道長勇蝙,這世上最難降的妖魔是什么沫勿? 我笑而不...
    開封第一講書人閱讀 58,165評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮味混,結(jié)果婚禮上藕帜,老公的妹妹穿的比我還像新娘。我一直安慰自己惜傲,他們只是感情好洽故,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評(píng)論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著盗誊,像睡著了一般时甚。 火紅的嫁衣襯著肌膚如雪隘弊。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,146評(píng)論 1 297
  • 那天荒适,我揣著相機(jī)與錄音梨熙,去河邊找鬼。 笑死刀诬,一個(gè)胖子當(dāng)著我的面吹牛咽扇,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播陕壹,決...
    沈念sama閱讀 40,032評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼质欲,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了糠馆?” 一聲冷哼從身側(cè)響起嘶伟,我...
    開封第一講書人閱讀 38,896評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎又碌,沒想到半個(gè)月后九昧,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,311評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡毕匀,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評(píng)論 2 332
  • 正文 我和宋清朗相戀三年铸鹰,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片皂岔。...
    茶點(diǎn)故事閱讀 39,696評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡掉奄,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出凤薛,到底是詐尸還是另有隱情姓建,我是刑警寧澤,帶...
    沈念sama閱讀 35,413評(píng)論 5 343
  • 正文 年R本政府宣布缤苫,位于F島的核電站速兔,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏活玲。R本人自食惡果不足惜涣狗,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評(píng)論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望舒憾。 院中可真熱鬧镀钓,春花似錦、人聲如沸镀迂。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽探遵。三九已至窟赏,卻和暖如春妓柜,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背涯穷。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評(píng)論 1 269
  • 我被黑心中介騙來泰國打工棍掐, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人拷况。 一個(gè)月前我還...
    沈念sama閱讀 47,698評(píng)論 2 368
  • 正文 我出身青樓作煌,卻偏偏與公主長得像,于是被迫代替她去往敵國和親赚瘦。 傳聞我的和親對(duì)象是個(gè)殘疾皇子粟誓,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容

  • 【你到底有沒有資本】重點(diǎn)摘錄: 錢本身頂多可以算作資金,而它想要成為有效的資本蚤告,還有需要至少另外三個(gè)因素: 1努酸、 ...
    小小子琦閱讀 316評(píng)論 0 1
  • 人潮擁擠 我能感覺你 感謝相遇 感謝生命中有你 停頓片刻 用心聆聽 聆聽內(nèi)心深處 最真實(shí)的聲音 停頓片刻 稍作調(diào)整...
    丹丹是個(gè)膽小鬼閱讀 431評(píng)論 2 6
  • 1. window.location.hash實(shí)現(xiàn)ajax操作或者一些點(diǎn)擊變化邏輯瀏覽器的前進(jìn)后退 參考原文htt...
    冰紅茶ht閱讀 337評(píng)論 0 1
  • 今天比較特殊,10月1日仍源,祖國的生日心褐,早上就想著今天應(yīng)該做點(diǎn)什么,此刻決定給自己的2016年剩下的這幾個(gè)月定個(gè)寫作...
    biliali閱讀 210評(píng)論 1 0