JDK8新特性:
1.Lambda表達式
2.新的日期API
3.引入Optional
4.使用Base64
5.接口的默認方法和靜態方法
6.新增方法引用格式
7.新增Stream類
8.注解相關的改變
9.支持並行(parallel)數組
10.對並發類(Concurrency)的擴展。
一、Lambda表達式
Lambda 表達式也可稱爲閉包,是推動 Java 8 發布的最重要新特性。lambda表達式本質上是一個匿名方法。Lambda允許把函數作爲一個方法的參數(函數作爲參數傳遞進方法中)或者把代碼看成數據。使用 Lambda 表達式可以使代碼變的更加簡潔緊湊。在最簡單的形式中,一個lambda可以由:用逗號分隔的參數列表、–>符號、函數體三部分表示,在某些情況下lambda的函數體會更加複雜,這時可以把函數體放到在一對花括號中,就像在Java中定義普通函數一樣。Lambda可以引用類的成員變量與局部變量(如果這些變量不是final的話,它們會被隱含的轉爲final,這樣效率更高)。Lambda可能會返回一個值。返回值的類型也是由編譯器推測出來的。如果lambda的函數體只有一行的話,那麽沒有必要顯式使用return語句。
如何使現有的函數友好地支持lambda。最終采取的方法是:增加函數式接口的概念。函數式接口就是接口裏面必須有且只有一個抽象方法的普通接口,像這樣的接口可以被隱式轉換爲lambda表達式成爲函數式接口。 在可以使用lambda表達式的地方,方法聲明時必須包含一個函數式的接口。 任何函數式接口都可以使用lambda表達式替換,例如:ActionListener、Comparator、Runnable。
函數式接口是容易出錯的:如有某個人在接口定義中增加了另一個方法,這時,這個接口就不再是函數式的了,並且編譯過程也會失敗。爲了克服函數式接口的這種脆弱性並且能夠明確聲明接口作爲函數式接口的意圖,Java 8增加了一種特殊的注解@FunctionalInterface,但是默認方法與靜態方法並不影響函數式接口的契約,可以任意使用。
使用lambda表達式替換匿名類,而實現Runnable接口是匿名類的最好示例。通過() -> {}代碼塊替代了整個匿名類。
java 8之前:
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(“Before Java8, too much code for too little to do”);
}
}).start();
Java 8方式:
new Thread( () -> System.out.println(“In Java8, Lambda expression rocks !!”) ).start();
Lambda 表達式免去了使用匿名方法的麻煩,並且給予Java簡單但是強大的函數化的編程能力。
二、新的日期API
Java 8通過發布新的Date-Time API (JSR 310)來進一步加強對日期與時間的處理。在舊版的 Java 中,日期時間 API 存在諸多問題,比如:
1.非線程安全 − java.util.Date 是非線程安全的,所有的日期類都是可變的,這是Java日期類最大的問題之一。
2.設計很差 − Java的日期/時間類的定義並不一致,在java.util和java.sql的包中都有日期類,此外用于格式化和解析的類在java.text包中定義。java.util.Date同時包含日期和時間,而java.sql.Date僅包含日期,將其納入java.sql包並不合理。另外這兩個類都有相同的名字,這本身就是一個非常糟糕的設計。
3.時區處理麻煩 − 日期類並不提供國際化,沒有時區支持,因此Java引入了java.util.Calendar和java.util.TimeZone類,但他們同樣存在上述所有的問題。
Java 8 在 java.time 包下提供了很多新的 API。以下爲兩個比較重要的 API:
1.Local(本地) − 簡化了日期時間的處理,沒有時區的問題。
2.Zoned(時區) − 通過制定的時區處理日期時間。
新的java.time包涵蓋了所有處理日期,時間,日期/時間,時區,時刻(instants),過程(during)與時鍾(clock)的操作。
三、Optional
Optional類實際上是個容器:它可以保存類型T的值,或者僅僅保存null。Optional 類的引入很好的解決空指針異常。Optional提供很多有用的方法,這樣我們就不用顯式進行空值檢測。盡量避免在程序中直接調用Optional對象的get()和isPresent()方法,避免使用Optional類型聲明實體類的屬性。
(1)Optional.of(T t) : 創建一個 Optional 實例
(2)Optional.empty() : 創建一個空的 Optional 實例
(3)Optional.ofNullable(T t):若 t 不爲 null,創建 Optional 實例,否則創建空實例
(4)isPresent() : 判斷是否包含值 orElse(T t) : 如果調用對象包含值,返回該值,否則返回t
(5)orElseGet(Supplier s) :如果調用對象包含值,返回該值,否則返回 s 獲取的值
(6)map(Function f): 如果有值對其處理,並返回處理後的Optional,否則返回Optional.empty()
(7)flatMap(Function mapper):與 map 類似,要求返回值必須是Optional
1.創建optional對象,一般用ofNullable()而不用of():
(1)empty() :用于創建一個沒有值的Optional對象:Optional<String> emptyOpt = Optional.empty();
(2)of() :使用一個非空的值創建Optional對象:Optional<String> notNullOpt = Optional.of(str);
(3)ofNullable() :接收一個可以爲null的值:Optional<String> nullableOpt = Optional.ofNullable(str);
2.判斷Null:
(1)isPresent():如果創建的對象實例爲非空值的話,isPresent()返回true,調用get()方法會返回該對象,如果沒有值,調用isPresent()方法會返回false,調用get()方法抛出NullPointerException異常。
3.獲取對象:
(1)get()
4.使用map提取對象的值,如果我們要獲取User對象中的roleId屬性值,常見的方式是先判斷是否爲null然後直接獲取,但使用Optional中提供的map()方法可以以更簡單的方式實現
5.使用orElse方法設置默認值,Optional類還包含其他方法用于獲取值,這些方法分別爲:
(1)orElse():如果有值就返回,否則返回一個給定的值作爲默認值;
(2)orElseGet():與orElse()方法作用類似,區別在于生成默認值的方式不同。該方法接受一個Supplier<? extends T>函數式接口參數,用于生成默認值;
(3)orElseThrow():與前面介紹的get()方法類似,當值爲null時調用這兩個方法都會抛出NullPointerException異常,區別在于該方法可以指定抛出的異常類型。
6.使用filter()方法過濾,filter()方法可用于判斷Optional對象是否滿足給定條件,一般用于條件過濾,在代碼中,如果filter()方法中的Lambda表達式成立,filter()方法會返回當前Optional對象值,否則,返回一個值爲空的Optional對象。:
四、Base64
Base64編碼的作用 :
由于某些系統中只能使用ASCII字符。Base64就是用來將非ASCII字符的數據轉換成ASCII字符的一種方法。 Base64其實不是安全領域下的加密解密算法,而是一種編碼,也就是說,它是可以被翻譯回原來的樣子。它並不是一種加密過程。所以base64只能算是一個編碼算法,對數據內容進行編碼來適合傳輸。雖然base64編碼過後原文也變成不能看到的字符格式,但是這種方式很初級,很簡單。
使用Base64編碼原因 :
1.base64是網絡上最常見的用于傳輸8bit字節代碼的編碼方式之一。有時我們需要把二進制數據編碼爲適合放在URL中的形式。這時采用base64編碼具有不可讀性,即所編碼的數據不會被人直接看出。
2.用于在http環境下傳遞較長的標識信息。
在Java 8中,Base64編碼已經成爲Java類庫的標准,並內置了 Base64 編碼的編碼器和解碼器。Base64工具類提供了一套靜態方法獲取下面三種BASE64編解碼器:
基本:輸出被映射到一組字符A-Za-z0-9+/,編碼不添加任何行標,輸出的解碼僅支持A-Za-z0-9+/。
URL:輸出映射到一組字符A-Za-z0-9+_,輸出是URL和文件。
MIME:輸出隱射到MIME友好格式。輸出每行不超過76字符,並且使用’\r’並跟隨’\n’作爲分割。編碼輸出最後沒有行分割。
五、接口的默認方法和靜態方法
Java 8用默認方法與靜態方法這兩個新概念來擴展接口的聲明。默認方法與抽象方法不同之處在于抽象方法必須要求實現,但是默認方法則沒有這個要求,就是接口可以有實現方法,而且不需要實現類去實現其方法。我們只需在方法名前面加個default關鍵字即可實現默認方法。爲什麽要有這個特性?以前當需要修改接口的時候,需要修改全部實現該接口的類。而引進的默認方法的目的是爲了解決接口的修改與現有的實現不兼容的問題。
默認方法語法格式如下:
public interface Vehicle {
default void print(){
System.out.println(“我是一輛車!”);
}
}
當出現這樣的情況,一個類實現了多個接口,且這些接口有相同的默認方法,這種情況的解決方法:
1.是創建自己的默認方法,來覆蓋重寫接口的默認方法
2.可以使用 super 來調用指定接口的默認方法
Java 8 的另一個特性是接口可以聲明(並且可以提供實現)靜態方法。在JVM中,默認方法的實現是非常高效的,並且通過字節碼指令爲方法調用提供了支持。默認方法允許繼續使用現有的Java接口,而同時能夠保障正常的編譯過程。盡管默認方法非常強大,但是在使用默認方法時我們需要小心注意一個地方:在聲明一個默認方法前,請仔細思考是不是真的有必要使用默認方法,因爲默認方法會帶給程序歧義,並且在複雜的繼承體系中容易産生編譯錯誤。
六、方法引用
方法引用提供了非常有用的語法,可以直接引用已有Java類或對象(實例)的方法或構造器。與lambda聯合使用,方法引用可以使語言的構造更緊湊簡潔,減少冗余代碼。
定義了4個方法的Car這個類作爲例子,區分Java中支持的4種不同的方法引用。
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() );
}
}
第一種方法引用是構造器引用,它的語法是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 );
七、Stream
Java 8 API添加了一個新的抽象稱爲流Stream把真正的函數式編程風格引入到Java中,可以讓你以一種聲明的方式處理數據。Stream 使用一種類似用 SQL 語句從數據庫查詢數據的直觀方式來提供一種對 Java 集合運算和表達的高階抽象。Stream API極大簡化了集合框架的處理,這種風格將要處理的元素集合看作一種流, 流在管道中傳輸, 並且可以在管道的節點上進行處理, 比如篩選, 排序,聚合等。
Stream流有一些特性:
1.Stream流不是一種數據結構,不保存數據,它只是在原數據集上定義了一組操作。
2.這些操作是惰性的,即每當訪問到流中的一個元素,才會在此元素上執行這一系列操作。
3.Stream不保存數據,故每個Stream流只能使用一次。
所以這邊有兩個概念:流、管道。元素流在管道中經過中間操作的處理,最後由最終操作得到前面處理的結果。這裏有2個操作:中間操作、最終操作。
中間操作:返回結果都是Stream,故可以多個中間操作疊加。
終止操作:用于返回我們最終需要的數據,只能有一個終止操作。
使用Stream流,可以清楚地知道我們要對一個數據集做何種操作,可讀性強。而且可以很輕松地獲取並行化Stream流,不用自己編寫多線程代碼,可以更加專注于業務邏輯。默認情況下,從有序集合、生成器、叠代器産生的流或者通過調用Stream.sorted産生的流都是有序流,有序流在並行處理時會在處理完成之後恢複原順序。無限流的存在,側面說明了流是惰性的,即每當用到一個元素時,才會在這個元素上執行這一系列操作。
使用Stream的基本步驟:
1.創建Stream
2.轉換Stream,每次轉換原有Stream對象不改變,返回一個新的Stream對象(可以有多次轉換)
3.對Stream進行聚合操作,獲取想要的結果
一、 流的生成方法
1.Collection接口的stream()或parallelStream()方法
2.靜態的Stream.of()、Stream.empty()方法
3.Arrays.stream(array, from, to)
4.靜態的Stream.generate()方法生成無限流,接受一個不包含引元的函數
5.靜態的Stream.iterate()方法生成無限流,接受一個種子值以及一個叠代函數
6.Pattern接口的splitAsStream(input)方法
7.靜態的Files.lines(path)、Files.lines(path, charSet)方法
8.靜態的Stream.concat()方法將兩個流連接起來
二、 流的Intermediate方法(中間操作)
1.filter(Predicate) :將結果爲false的元素過濾掉
2.map(fun) :轉換元素的值,可以用方法引元或者lambda表達式
3.flatMap(fun) :若元素是流,將流攤平爲正常元素,再進行元素轉換
4.limit(n) :保留前n個元素
5.skip(n) :跳過前n個元素
6.distinct() :剔除重複元素
7.sorted() :將Comparable元素的流排序
8.sorted(Comparator) :將流元素按Comparator排序
9.peek(fun) :流不變,但會把每個元素傳入fun執行,可以用作調試
三、 流的Terminal方法(終結操作)
(1)約簡操作
1.reduce(fun) :從流中計算某個值,接受一個二元函數作爲累積器,從前兩個元素開始持續應用它,累積器的中間結果作爲第一個參數,流元素作爲第二個參數
2.reduce(a, fun) :a爲幺元值,作爲累積器的起點
3.reduce(a, fun1, fun2) :與二元變形類似,並發操作中,當累積器的第一個參數與第二個參數都爲流元素類型時,可以對各個中間結果也應用累積器進行合並,但是當累積器的第一個參數不是流元素類型而是類型T的時候,各個中間結果也爲類型T,需要fun2來將各個中間結果進行合並
(2)收集操作
1.iterator():
2.forEach(fun):
3.forEachOrdered(fun) :可以應用在並行流上以保持元素順序
4.toArray():
5.toArray(T[] :: new) :返回正確的元素類型
6.collect(Collector):
7.collect(fun1, fun2, fun3) :fun1轉換流元素;fun2爲累積器,將fun1的轉換結果累積起來;fun3爲組合器,將並行處理過程中累積器的各個結果組合起來
(3)查找與收集操作
1.max(Comparator):返回流中最大值
2.min(Comparator):返回流中最小值
3.count():返回流中元素個數
4.findFirst() :返回第一個元素
5.findAny() :返回任意元素
6.anyMatch(Predicate) :任意元素匹配時返回true
7.allMatch(Predicate) :所有元素匹配時返回true
8.noneMatch(Predicate) :沒有元素匹配時返回true
八、注解相關
(1)可以進行重複注解
自從Java 5引入了注解機制,這一特性就變得非常流行並且廣爲使用。然而,使用注解的一個限制是相同的注解在同一位置只能聲明一次,不能聲明多次。Java 8打破了這條規則,引入了重複注解機制,這樣相同的注解可以在同一地方聲明多次。
重複注解機制本身必須用@Repeatable注解。事實上,這並不是語言層面上的改變,更多的是編譯器的技巧,底層的原理保持不變。
(2)擴展注解的支持
Java 8擴展了注解的上下文。現在幾乎可以爲任何東西添加注解:局部變量、泛型類、父類與接口的實現,就連方法的異常也能添加注解。
九、並行(parallel)數組
Java 8增加了大量的新方法來對數組進行並行處理。可以說,最重要的是parallelSort()方法,因爲它可以在多核機器上極大提高數組排序的速度。下面的例子展示了新方法(parallelXxx)的使用。
上面的代碼片段使用了parallelSetAll()方法來對一個有20000個元素的數組進行隨機賦值。然後,調用parallelSort方法。這個程序首先打印出前10個元素的值,之後對整個數組排序。這個程序在控制台上的輸出如下(請注意數組元素是隨機生産的):
十、並發(Concurrency)
在新增Stream機制與lambda的基礎之上,在java.util.concurrent.ConcurrentHashMap中加入了一些新方法來支持聚集操作。同時也在java.util.concurrent.ForkJoinPool類中加入了一些新方法來支持共有資源池(common pool)(請查看我們關于Java 並發的免費課程)。
新增的java.util.concurrent.locks.StampedLock類提供一直基于容量的鎖,這種鎖有三個模型來控制讀寫操作(它被認爲是不太有名的java.util.concurrent.locks.ReadWriteLock類的替代者)。
在java.util.concurrent.atomic包中還增加了下面這些類:
1.DoubleAccumulator
2.DoubleAdder
3.LongAccumulator
4.LongAdder
————————————————
版權聲明:本文爲CSDN博主「ZytheMoon」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/ZytheMoon/article/details/89715618