Java 8功能教程– ULTIMATE指南(PDF下载)
編者注:在本文中,我們提供了全面的Java 8功能教程。 自Java 8公開發布以來已經有一段時間了,所有跡象都表明這是一個非常重要的版本。
我們在Java Code Geeks處提供了大量教程,例如“ 玩Java 8 – Lambda和并發” ,“ Java 8 Date Time API教程: JDK 8時代的 LocalDateTime和抽象類與接口” 。
我們還引用了其他來源的15篇必讀Java 8教程 。 當然,我們還研究了一些不足之處,例如Java 8的黑暗面 。
現在,是時候將所有Java 8的主要功能集中到一本參考文章中,以使您閱讀愉快。 請享用!
目錄
1.簡介 2. Java語言的新功能1.簡介
毫無疑問, Java 8發行版是Java 5以來(Java發行版早在2004年發布)以來最偉大的事情。 它為Java帶來了許多新功能,包括一種語言,其編譯器,庫,工具以及JVM(Java虛擬機)本身。 在本教程中,我們將研究所有這些更改,并在實際示例中演示不同的使用場景。
本教程由幾個部分組成,每個部分都涉及平臺的特定方面:
- 語言
- 編譯器
- 圖書館
- 工具
- 運行時(JVM)
2. Java語言的新功能
Java 8無論如何都是主要版本。 可能有人說,要實現每個Java開發人員正在尋找的功能,最終確定都花了很長時間。 在本節中,我們將介紹其中的大部分內容。
Lambda和功能接口
Lambda(也稱為閉包)是整個Java 8版本中最大,最期待的語言更改。 它們使我們可以將功能視為方法參數(傳遞函數),或將代碼視為數據:每個功能開發人員都非常熟悉的概念。 從第一天開始,JVM平臺上的許多語言(Groovy, Scala等等)都具有lambda,但是Java開發人員別無選擇,只能用樣板匿名類來破壞lambda。
Lambdas設計討論花費了大量時間和社區精力。 但是最后,我們找到了折衷方案,從而導致了新的簡潔緊湊的語言結構。 以最簡單的形式,lambda可以表示為以逗號分隔的參數列表,即–>符號和主體。 例如:
Arrays.asList( "a" , "b" , "d" ).forEach( e -> System.out.println( e ) );請注意,參數e的類型是由編譯器推斷的。 或者,您可以顯式提供參數的類型,并將定義包裝在方括號中。 例如:
Arrays.asList( "a" , "b" , "d" ).forEach( ( String e ) -> System.out.println( e ) );如果lambda的主體更復雜,則可以將其包裝在方括號中,就像Java中常用的函數定義一樣。 例如:
Arrays.asList( "a" , "b" , "d" ).forEach( e -> { System.out.print( e ); System.out.print( e ); } );Lambda可以引用類成員和局部變量(如果沒有,則暗指使它們有效地成為最終變量)。 例如,這兩個片段是等效的:
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可能會返回一個值。 返回值的類型將由編譯器推斷。 如果lambda主體是單線的,則不需要return語句。 以下兩個代碼段是等效的:
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; } );語言設計師對如何使已經存在的功能實現lambda友好性進行了大量思考。 結果,出現了功能接口的概念。 函數接口是只有一種方法的接口。 這樣,它可以隱式轉換為lambda表達式。 java.lang.Runnable和java.util.concurrent.Callable是功能接口的兩個很好的例子。 實際上,功能接口是易碎的:如果有人在接口定義中僅添加了另一種方法,它將不再起作用,并且編譯過程將失敗。 為了克服這種脆弱性并明確聲明接口的作用,Java 8添加了特殊的注解@FunctionalInterface(Java庫中所有現有的接口也已使用@FunctionalInterface進行注解)。 讓我們看一下這個簡單的功能接口定義:
@FunctionalInterface public interface Functional { void method(); }要記住的一件事: 默認方法和靜態方法不會破壞功能接口協定,可以聲明為:
@FunctionalInterface public interface FunctionalDefaultMethods { void method(); ????????default void defaultMethod() { } }Lambda是Java 8的最大賣點。它有潛力吸引越來越多的開發人員使用這個強大的平臺,并為純Java中的函數式編程概念提供最新的支持。 有關更多詳細信息,請參閱官方文檔 。
接口的默認方法和靜態方法
Java 8用兩個新概念擴展了接口聲明:默認方法和靜態方法。 默認方法使接口在某種程度上類似于特征,但目標有所不同。 它們允許向現有接口添加新方法,而不會破壞與為這些接口的較早版本編寫的代碼的二進制兼容性。
默認方法和抽象方法之間的區別在于,需要實現抽象方法。 但是默認方法不是。 相反,每個接口都必須提供所謂的默認實現,并且所有實現者都將默認繼承它(可以在需要時覆蓋此默認實現)。 讓我們看下面的例子。
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使用關鍵字default作為方法定義的一部分來聲明默認方法notRequired() 。 其中一個類DefaultableImpl實現了此接口,而保留了默認方法的實現。 另一個OverridableImpl重寫默認實現并提供自己的實現。
Java 8提供的另一個有趣的功能是接口可以聲明(并提供實現)靜態方法。 這是一個例子。
private interface DefaulableFactory { // Interfaces now allow static methods static Defaulable create( Supplier< Defaulable > supplier ) { return supplier.get(); } }下面的小代碼段將上面示例中的默認方法和靜態方法粘合在一起。
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() ); }該程序的控制臺輸出如下所示:
Default implementation Overridden implementationJVM上的默認方法實現非常有效,并且方法調用的字節碼指令支持該方法。 默認方法允許現有的Java接口發展而不會中斷編譯過程。 很好的例子是添加到java.util.Collection接口的大量方法: stream() , parallelStream() , forEach() , removeIf() ……
盡管功能強大,但應謹慎使用默認方法:在將方法聲明為默認方法之前,最好三思,如果確實需要,則可能會導致復雜層次結構中的歧義和編譯錯誤。 有關更多詳細信息,請參閱官方文檔 。
方法參考
方法引用提供了有用的語法,可以直接引用現有的方法或Java類或對象(實例)的構造函數。 與Lambdas表達式結合使用時,方法引用使語言構造看起來緊湊而簡潔,從而簡化了樣板。
下面,將Car類作為不同方法定義的示例,讓我們區分四種受支持的方法引用類型。
public static Car { 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() ); } }方法引用的第一類是語法為Class :: new的構造方法引用,或者對于泛型,為Class <T> :: new 。 請注意,構造函數沒有參數。
final Car car = Car.create( Car:: new ); final List< Car > cars = Arrays.asList( car );第二種類型是使用語法Class :: static_method引用靜態方法。 請注意,該方法僅接受Car類型的一個參數。
cars.forEach( Car::collide );第三種類型是使用語法Class :: method引用特定類型的任意對象的實例方法 。 請注意,該方法不接受任何參數。
cars.forEach( Car::repair );最后,第四種類型是對特定類實例的實例方法的引用,即語法instance :: method 。 請注意,該方法只接受Car類型的一個參數。
final Car police = Car.create( Car:: new ); cars.forEach( police::follow );將所有這些示例作為Java程序運行會在控制臺上產生以下輸出(實際的Car實例可能有所不同):
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有關方法引用的更多示例和詳細信息,請參考官方文檔。
重復注釋
自從Java 5引入了注釋支持以來,此功能就非常流行并且得到了廣泛的使用。 但是,注釋使用的局限性之一是不能在同一位置多次聲明同一注釋。 Java 8違反了此規則,并引入了重復注釋。 它允許相同的注釋在聲明的位置重復多次。
重復的注釋本身應使用@Repeatable注釋進行注釋。 實際上,這不是語言更改,而是更多的編譯器技巧,因為該技術的原理保持不變。 讓我們看一個簡單的例子:
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() ); } } }正如我們所看到的,是一個注釋類過濾器與@Repeatable(過濾器。 類 )注解。 過濾器只是過濾器注釋的持有者,但是Java編譯器試圖向開發人員隱藏其存在。 因此, Filterable接口具有兩次定義的Filter注釋(沒有提及Filters )。
此外,反射API提供了新的方法getAnnotationsByType()返回某種類型的重復注釋(請注意,篩選。 類 .getAnnotation(過濾器。 類 )將返回過濾器的編譯器注入的情況下)。
程序輸出如下所示:
filter1 filter2有關更多詳細信息,請參閱官方文檔 。
更好的類型推斷
Java 8編譯器在類型推斷方面進行了很多改進。 在許多情況下,可以通過使代碼保持整潔的編譯器來推斷顯式類型參數。 讓我們看一個例子。
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; ) ? value : defaultValue; } }這是Value <String>類型的用法。
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() ); } }Value的類型參數。 推斷出defaultValue () ,不需要提供該值。 在Java 7中,相同的示例將無法編譯,應將其重寫為Value。<String> defaultValue () 。
擴展注釋支持
Java 8擴展了可以使用注釋的上下文。 現在,幾乎可以注釋所有內容:局部變量,泛型類型,超類和實現接口,甚至是方法的異常聲明。 下面是幾個示例。
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<>(); } }ElementType。 TYPE_USE和ElementType。 TYPE_PARAMETER是兩種新的元素類型,用于描述適用的注釋上下文。 Annotation Processing API也進行了一些小的更改,以識別Java編程語言中的那些新類型的注釋。
3. Java編譯器的新功能
參數名稱
從字面上看,Java開發人員已經發明了各種方法來將方法參數名稱保留為Java字節碼 ,并使它們在運行時可用(例如Paranamer庫 )。 最后,Java 8將此要求苛刻的功能烘焙到語言中(使用Reflection API和Parameter.getName()方法)和字節碼(使用新的javac編譯器參數–parameters )。
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() ); } } }如果在不使用–parameters參數的情況下編譯此類,然后運行該程序,則會看到類似以下內容:
Parameter: arg0將–parameters參數傳遞給編譯器后,程序輸出將有所不同(將顯示參數的實際名稱):
Parameter: args對于有經驗的Maven用戶 ,可以使用maven-compiler-plugin的 configuration部分將–parameters參數添加到編譯器中 :
< 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 >帶有Java 8的最新Eclipse Kepler SR2版本(請查看此下載說明 )支持提供了有用的配置選項來控制此編譯器設置,如下圖所示。
圖1.配置Eclipse項目以支持新的Java 8編譯器–parameters參數另外,為了驗證參數名稱的可用性, Parameter類提供了一種方便的方法isNamePresent() 。
4. Java庫中的新功能
Java 8添加了許多新類并擴展了現有類,以便為現代并發,函數式編程,日期/時間等提供更好的支持。
可選的
迄今為止, 著名的NullPointerException是Java應用程序失敗的最常見原因。 很久以前,偉大的Google Guava項目引入了Optional作為NullPointerException的解決方案,通過空檢查防止代碼庫污染,并鼓勵開發人員編寫更干凈的代碼。 受Google Guava的啟發, Optional現在是Java 8庫的一部分。
可選的只是一個容器:它可以保存某個T類型的值 ,或者可以為null 。 它提供了許多有用的方法,因此顯式null檢查不再有任何借口。 請參閱Java 8官方文檔以獲取更多詳細信息。
我們將看一下Optional用法的兩個小例子:具有可為空的值和不允許為null的值。
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的此實例具有非null值,則isPresent()方法返回true,否則返回false 。 如果Optional通過接受生成默認值的函數為null ,則orElseGet()方法提供了后備機制。 map()方法轉換當前Optional的值,并返回新的Optional實例。 orElse()方法類似于orElseGet(),但它接受函數默認值而不是函數。 這是該程序的輸出:
Full Name is set? false Full Name: [none] Hey Stranger!讓我們簡要地看一下另一個例子:
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 is set? true First Name: Tom Hey Tom!有關更多詳細信息,請參閱官方文檔。
流
新添加的Stream API ( java.util.stream )在Java 中引入了現實世界中的函數式編程。 到目前為止,這是Java庫中最全面的擴展,旨在通過允許Java開發人員編寫有效,簡潔,簡潔的代碼來提高他們的生產力。
Stream API大大簡化了集合的處理(但不僅限于Java集合,我們將在后面看到)。 讓我們從名為Task的簡單類開始。
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 ); } } }Task具有一些點(或偽復雜性)概念,可以為OPEN或CLOSED 。 然后,讓我們介紹一些要處理的任務。
final Collection< Task > tasks = Arrays.asList( new Task( Status.OPEN, 5 ), new Task( Status.OPEN, 13 ), new Task( Status.CLOSED, 8 ) );我們要解決的第一個問題是所有OPEN任務總共有多少點? 在Java 8之前,通常的解決方案是某種foreach迭代。 但是在Java 8中,答案是流:支持順序和并行聚合操作的一系列元素。
// 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 );控制臺上的輸出如下所示:
Total points: 18這里發生了幾件事。 首先,將任務集合轉換為其流表示形式。 然后,對流的過濾操作將所有已關閉的任務過濾掉。 上下一步驟中,mapToInt操作任務 S的整數 s使用每個任務實例的任務:: getPoints方法的流的流轉換。 最后,使用求和方法對所有點求和,得出最終結果。
在繼續下一個示例之前,需要注意一些有關流的注意事項( 此處有更多詳細信息 )。 流操作分為中間操作和終端操作。
中間操作返回一個新的流。 它們總是很懶惰,執行諸如filter之類的中間操作實際上并不執行任何過濾,而是創建一個新的流,該新流在遍歷時將包含與給定謂詞匹配的初始流的元素。
終端操作(例如forEach或sum )可能會遍歷流以產生結果或副作用。 執行終端操作后,流管道被視為已消耗,無法再使用。 在幾乎所有情況下,終端操作人員都很渴望完成對基礎數據源的遍歷。
流的另一個價值主張是開箱即用的并行處理支持。 讓我們看一下這個示例,該示例確實總結了所有任務的要點。
// 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 );除了我們嘗試并行處理所有任務并使用reduce方法計算最終結果外,它與第一個示例非常相似。
這是控制臺輸出:
Total points (all tasks): 26.0通常,需要根據某些標準對收集元素進行分組。 流可以幫助實現這一點,下面的示例也將對此進行說明。
// Group tasks by their status final Map< Status, List< Task > > map = tasks .stream() .collect( Collectors.groupingBy( Task::getStatus ) ); System.out.println( map );此示例的控制臺輸出如下所示:
{CLOSED=[[CLOSED, 8 ]], OPEN=[[OPEN, 5 ], [OPEN, 13 ]]}為了完成任務示例,讓我們基于任務集的點來計算每個任務在整個集合中的總體百分比(或權重)。
// 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 * // LongStream )( weigth * 100 ) ) // LongStream .mapToObj( percentage -> percentage + "%" ) // Stream< String> .collect( Collectors.toList() ); // List< String > ???????? System.out.println( result );控制臺輸出就在這里:
[ 19 %, 50 %, 30 %]最后,如前所述,Stream API不僅與Java集合有關。 像逐行讀取文本文件這樣的典型I / O操作非常適合從流處理中受益。 這是一個確認這一點的小例子。
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 ); }在流上調用的onClose方法返回具有附加關閉處理程序的等效流。 在流上調用close()方法時,將運行關閉處理程序。
通過接口的默認方法和靜態方法生成的 Stream API以及Lambda和方法引用是Java 8對軟件開發中現代范例的響應。 有關更多詳細信息,請參閱官方文檔 。
日期/時間API(JSR 310)
Java 8通過提供新的Date-Time API(JSR 310),進一步進行了日期和時間管理。 對于Java開發人員而言,日期和時間操縱是最糟糕的痛苦之一。 緊隨其后的java.util.Calendar標準java.util.Date根本沒有改善這種情況(可以說,使情況更加混亂)。
這就是Joda-Time的誕生方式:Java的絕佳替代日期/時間API。 Java 8的新Date-Time API(JSR 310)受Joda-Time的影響很大,并充分利用了它的優勢。 新的java.time包包含日期,時間,日期/時間,時區,瞬間,持續時間和時鐘操作的所有類 。 在API的設計中,非常認真地考慮了不變性:不允許進行任何更改(從java.util.Calendar中吸取的艱巨教訓)。 如果需要修改,將返回相應類的新實例。
讓我們看一下關鍵類及其用法示例。 第一類是時鐘 ,它使用時區提供對當前時刻,日期和時間的訪問。 可以使用Clock代替System.currentTimeMillis()和TimeZone.getDefault() 。
// Get the system clock as UTC offset final Clock clock = Clock.systemUTC(); System.out.println( clock.instant() ); System.out.println( clock.millis() );控制臺上的示例輸出:
2014 - 04 2014 -12T15: 19 : 29 .282Z 1397315969360我們將要查看的其他新類是LocaleDate和LocalTime 。 在ISO- 8601日歷系統中,LocaleDate僅保存日期部分,沒有時區。 分別地, LocaleTime在ISO- 8601日歷系統中僅保存沒有時區的時間部分 。 LocaleDate和LocaleTime都可以從Clock創建。
// 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 );控制臺上的示例輸出:
2014 - 04 - 12 2014 - 04 - 12 11 : 25 : 54.568 15 : 25 : 54.568LocalDateTime將LocaleDate和LocalTime結合在一起,并在ISO-8601日歷系統中保存帶時間的日期,但沒有時區。 快速示例如下所示。
// Get the local date/time final LocalDateTime datetime = LocalDateTime.now(); final LocalDateTime datetimeFromClock = LocalDateTime.now( clock ); ???????? System.out.println( datetime ); System.out.println( datetimeFromClock );控制臺上的示例輸出:
2014 - 04 2014 -12T11: 37 : 52.309 2014 - 04 2014 -12T15: 37 : 52.309如果您需要特定時區的日期/時間,則可以使用ZonedDateTime來提供幫助。 在ISO-8601日歷系統中,它保存帶有日期和時區的日期。 這是不同時區的幾個示例。
// 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 );控制臺上的示例輸出:
2014 - 04 -12T11: 47 : 01.017 - 04 : 00 [America/New_York] 2014 - 04 2014 -12T15: 47 : 01 .017Z 2014 - 04 -12T08: 47 : 01.017 - 07 : 00 [America/Los_Angeles]最后,讓我們看一下Duration類:以秒和納秒為單位的時間量。 計算兩個日期之間的差異非常容易。 讓我們來看看。
// 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() );上面的示例計算了2014 年4月16日至2015年 4月 16日這兩個日期之間的持續時間(以天和小時為單位)。 這是控制臺上的示例輸出:
Duration in days: 365 Duration in hours: 8783關于Java 8的新日期/時間API的總體印象是非常非常積極的。 部分是由于它經過了久經考驗的基礎( Joda-Time ),部分是因為這一次最終得到了認真解決,并聽到了開發人員的聲音。 有關更多詳細信息,請參閱官方文檔 。
Nashorn JavaScript引擎
Java 8附帶了新的Nashorn JavaScript引擎 ,該引擎允許在JVM上開發和運行某些類型JavaScript應用程序。 Nashorn JavaScript引擎只是javax.script.ScriptEngine的另一種實現,并且遵循相同的規則集,從而允許Java和JavaScript互操作。 這是一個小例子。
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;" ) );控制臺上的示例輸出:
jdk.nashorn.api.scripting.NashornScriptEngine Result: 2我們將在后面專門討論新Java工具的部分中回到Nashorn。
Base64
最后,隨著Java 8版本的發布, 對Base64編碼的支持已進入Java標準庫。 如以下示例所示,它非常易于使用。
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 ); } }程序運行的控制臺輸出顯示編碼和解碼的文本:
QmFzZTY0IGZpbmFsbHkgaW4gSmF2YSA4IQ== Base64 finally in Java 8 !Base64類( Base64。getUrlEncoder () / Base64。getUrlDecoder () , Base64。getMimeEncoder () / Base64。getMimeDecoder () )還提供URL友好的編碼器/解碼器和MIME友好的編碼器/解碼器。
平行陣列
Java 8版本增加了許多新方法來允許并行數組處理。 可以說,最重要的一個是parallelSort() ,它可以顯著加快多核計算機上的排序速度。 下面的小示例演示了該新方法家族( parallelXxx )的實際作用。
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(); } }這個小代碼段使用方法parallelSetAll()填充具有20000個隨機值的數組。 之后,將應用parallelSort() 。 程序在排序前后會輸出前10個元素,以確保數組真正有序。 示例程序輸出看起來像這樣(請注意,數組元素是隨機生成的):
Unsorted: 591217 891976 443951 424479 766825 351964 242997 642839 119108 552378 Sorted: 39 220 263 268 325 607 655 678 723 793并發
新方法已添加到java.util.concurrent.ConcurrentHashMap類中,以支持基于新添加的流功能和lambda表達式的聚合操作。 另外,已經將新方法添加到java.util.concurrent.ForkJoinPool類中,以支持公用池(另請參見有關Java并發性的免費課程 )。
添加了新的java.util.concurrent.locks.StampedLock類,以提供具有三種模式的基于功能的鎖,用于控制讀/寫訪問(它可能被認為是臭名昭著的java.util.concurrent.locks.ReadWriteLock的替代方法。 )。
新類已添加到java.util.concurrent.atomic包中:
- 雙累加器
- 雙重加法器
- 長累積器
- 長加法器
5.新的Java工具
Java 8附帶了一組新的命令行工具。 在本節中,我們將研究其中最有趣的部分。
Nashorn引擎:jjs
jjs是基于命令行的獨立Nashorn引擎。 它接受JavaScript源代碼文件列表作為參數并運行它們。 例如,讓我們創建一個具有以下內容的文件func.js :
function f() { return 1 ; }; print( f() + 1 );要從命令執行此fie,讓我們將其作為參數傳遞給jjs :
jjs func.js控制臺上的輸出將是:
2有關更多詳細信息,請參閱官方文檔 。
類依賴分析器:jdeps
jdeps是一個非常出色的命令行工具。 它顯示了Java類文件的包級別或類級別的依賴關系。 它接受.class文件, 目錄或JAR文件作為輸入。 默認情況下, jdeps將依賴項輸出到系統輸出(控制臺)。
例如,讓我們看一下流行的Spring Framework庫的依賴項報告。 為了使示例簡短,讓我們僅分析一個JAR文件: org.springframework.core-3.0.5.RELEASE.jar 。
jdeps org.springframework.core- 3.0 . 5 .RELEASE.jar該命令輸出很多,因此我們將對其進行研究。 依賴項按程序包分組。 如果依賴項在類路徑上不可用,則顯示為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.utilFor more details please refer to official documentation .
6. New Features in Java runtime (JVM)
The PermGen space is gone and has been replaced with Metaspace ( JEP 122 ). The JVM options -XX:PermSize and – XX:MaxPermSize have been replaced by -XX:MetaSpaceSize and -XX:MaxMetaspaceSize respectively.
7.結論
The future is here: Java 8 moves this great platform forward by delivering the features to make developers much more productive. It is too early to move the production systems to Java 8 but in the next couples of months its adoption should slowly start growing. Nevertheless the time is right to start preparing your code bases to be compatible with Java 8 and to be ready to turn the switch once Java 8 proves to be safe and stable enough.
As a confirmation of community Java 8 acceptance, recently Pivotal released Spring Framework 4.0.3 with production-ready Java 8 support .
如果您喜歡此功能,請訂閱我們的時事通訊,以享受每周更新和免費白皮書! 另外,請查看我們的課程以獲得更高級的培訓!
You are welcome to contribute with your comments about the exciting new Java 8 features!
8. Resources
Some additional resources which discuss in depth different aspects of Java 8 features:
- Java 8 Tutorials on JCG Examples: https://examples.javacodegeeks.com/?s=java+8
- What's New in JDK 8: http://www.oracle.com/technetwork/java/javase/8-whats-new-2157071.html
- The Java Tutorials: http://docs.oracle.com/javase/tutorial/
- WildFly 8, JDK 8, NetBeans 8, Java EE 7: http://blog.arungupta.me/2014/03/wildfly8-jdk8-netbeans8-javaee7-excellent-combo-enterprise-java/
- Java 8 Tutorial: http://winterbe.com/posts/2014/03/16/java-8-tutorial/
- JDK 8 Command-line Static Dependency Checker: http://marxsoftware.blogspot.ca/2014/03/jdeps.html
- The Illuminating Javadoc of JDK 8: http://marxsoftware.blogspot.ca/2014/03/illuminating-javadoc-of-jdk-8.html
- The Dark Side of Java 8: http://blog.jooq.org/2014/04/04/java-8-friday-the-dark-side-of-java-8/
- Installing Java? 8 Support in Eclipse Kepler SR2: http://www.eclipse.org/downloads/java8/
- Java 8: http://www.baeldung.com/java8
- Oracle Nashorn. A Next-Generation JavaScript Engine for the JVM: http://www.oracle.com/technetwork/articles/java/jf14-nashorn-2126515.html
Java 8 Features Tutorial was last updated on Oct. 3, 2016
翻譯自: https://www.javacodegeeks.com/java-8-features-tutorial.html
總結
以上是生活随笔為你收集整理的Java 8功能教程– ULTIMATE指南(PDF下载)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 怎样增强路由器信号如何提升路由器功能
- 下一篇: Nashorn简介