本文內容如有錯誤、不足之處,歡迎技術愛好者們一同探討,在本文下面討論區留言,感謝。
文章目錄
簡述
Java 8 (又稱爲 jdk 1.8 ) 是 Java 語言開發的一個主要版本。 Oracle 公司於 2014 年 3 月 18 日發佈 Java 8 ,它支持函數式編程,新的 JavaScript 引擎,新的日期 API,新的 Stream API 等。
新功能
列表
- Iterable 接口中的 forEach() 方法
- 接口中的默認方法和靜態方法
- 功能接口和 Lambda 表達式
- Stream API 操作集合批量數據
- Date Time API 加強對日期與時間的處理
- 集合 API 改進
- 併發 API 改進
- Optional 類,解決空指針問題
- 新工具 新的編譯工具
詳細介紹
1. Iterable 接口中的 forEach() 方法
當程序需要遍歷 Collection 時,將會創建一個 Iterator 其目的是進行迭代集合的全部對象,然後針對 Collection 中的每個元素將業務邏輯循環聯繫在一起。如果迭代器使用不正確,程序會拋出 ConcurrentModificationException。
Java 8 在接口中引入了 forEach 方法,java.lang.Iterable 因此在編寫代碼時,開發人員僅需要關注業務邏輯即可。forEach 方法將 java.util.function.Consumer 對象作爲參數,因此有助於編寫可重用的業務邏輯代碼。
package iterable.java8;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class IterableDemo {
public static void main(String[] args) {
// 創建集合
List<Integer> myList = new ArrayList<Integer>();
for(int i=0; i<10; i++) myList.add(i);
// 使用 迭代器 iterator
Iterator<Integer> it = myList.iterator();
while(it.hasNext()){
Integer i = it.next();
System.out.println("Iterator Value::"+i);
}
// 使用forEach表達式
myList.forEach(t -> System.out.println("forEach anonymous class Value::"+t));
}
}
使用 java.util.function.Consumer 類
package iterable.java8;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.function.Consumer;
public class IterableConsumerDemo {
public static void main(String[] args) {
// 創建集合
List<Integer> myList = new ArrayList<Integer>();
for(int i=0; i<10; i++) myList.add(i);
// 使用 迭代器 iterator
Iterator<Integer> it = myList.iterator();
while(it.hasNext()){
Integer i = it.next();
System.out.println("Iterator Value::"+i);
}
// 使用forEach表達式,使用 Consumer
myList.forEach(new Consumer<Integer>() {
public void accept(Integer t) {
System.out.println("forEach anonymous class Value::"+t);
}
});
// 執行 子定義Consumer
MyConsumer action = new MyConsumer();
myList.forEach(action);
}
}
// 實現 Consumer 接口
class MyConsumer implements Consumer<Integer> {
public void accept(Integer t) {
System.out.println("Consumer Value::"+t);
}
}
Consumer 接口源碼:
/**
* 接受單個輸入參數且不返回結果的操作
* @since 1.8
*/
@FunctionalInterface
public interface Consumer<T> {
/**
* 對給定參數執行此操作。
*
* @param t 輸入參數,是個泛型
*/
void accept(T t);
/**
* 對給定參數執行此操作。
* 返回一個組合的 Consumer ,該組合 Consumer 將會依次執行accept方法定義的操作和 after 參數的 accept 操作。
* 如果執行任何一個操作都會引發異常,則會將該異常拋出到調用該組合操作的調用方。
* 如果執行此操作引發異常,不執行 after 參數的 accept 操作。
*
* @param after 當前對象執行accept操作後執行after中的accept操作 接口的默認實現
*/
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}
Iterable 源代碼
public interface Iterable<T> {
Iterator<T> iterator();
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
default Spliterator<T> spliterator() {
return Spliterators.spliteratorUnknownSize(iterator(), 0);
}
}
2. 接口中的默認方法和靜態方法
仔細閱讀上面的源碼,可以很容易發現 forEach() 方法是在 Iterable 接口中定義的,但是開發人員都很清楚接口不能具有方法主體。從 Java 8 開始,接口已增強爲具有實現的方法。使用 defaule 和 static 關鍵字來創建帶有方法實現的接口。
Java 不能在 Class 中提供多重繼承,因爲它會導致 Diamond 問題。由於接口現在類似於抽象類,因此現在如何使用接口處理它。在這種情況下,編譯器將引發異常,解決方案:在實現接口的類中提供實現邏輯。
定義一個接口 Interface1
package iterable.java8;
@FunctionalInterface
public interface Interface1 {
default void doSomething(String str){
System.out.println("I1 doSomething::"+str);
}
}
接口 Interface2
package iterable.java8;
@FunctionalInterface
public interface Interface2 {
default void doSomething(String str){
System.out.println("I2 doSomething::"+str);
}
}
實現接口 Interface1 和 Interface2
package iterable.java8;
public class InterfaceImplementDemo implements Interface1,Interface2 {
@Override
public void active(String str) {
}
/**
* 必須實現 log 這個方法
* @param str
*/
@Override
public void doSomething(String str) {
System.out.println("Implement doSomething::"+str);
}
}
Java 8 在 Collection API 中大量使用默認 default 和靜態 static 方法,並且添加了默認方法,以便代碼保持向下兼容。
3. 功能接口和 Lambda 表達式
什麼是功能接口?
具有一種抽象方法的接口,使用 @FunctionalInterface 註解進行表明。
功能接口的主要優點之一是可以使用 lambda 表達式實例化它們。之前可以使用匿名類實例化一個接口,但是這樣實現代碼看起來很龐大。
Runnable r = new Runnable(){
@Override
public void run() {
System.out.println(" Impetment Runnable");
}};
Runnable r1 = () -> {
System.out.println("Lambda Runnable");
};
因此,lambda 表達式可以輕鬆創建功能接口的匿名類的方法。由於,使用 lambda 表達式沒有運行時的好處,一方面調試困難,另一方面維護者需要閱讀整個 lambda 才能夠理解其邏輯,因此建議謹慎使用它。
4. Stream API 操作集合批量數據
新添加的Stream API(java.util.stream) 把真正的函數式編程風格引入到Java中。
Stream 是對集合對象功能的增強,專注於對集合對象進行便利、高效的操作,或者進行大批量數據操作 ,就是一種流式處理,所有的數據像一條河流一樣,Stream 中的函數就是針對這條數據河流進行相對應的處理,例如:filter 是對河流中符合對應的邏輯判斷進行篩選。
下面舉例說明:
public class StreamDemo {
public static void main(String[] args) {
List<Integer> listData = new ArrayList<>();
for (int i = 0; i < 30; i++) {
listData.add(i);
}
// 1. 獲取流
Stream<Integer> streamListData = listData.stream();
// 2. 過濾流 獲取集合中從1到10的數據集合
Stream<Integer> limit1To10 = streamListData.filter(p -> p >= 1 && p <= 10);
// 3. 打印信息
limit1To10.forEach(p -> System.out.println("limit1To10 Nums ="+p));
}
}
輸出結果:
limit1To10 Nums =1
limit1To10 Nums =2
limit1To10 Nums =3
limit1To10 Nums =4
limit1To10 Nums =5
limit1To10 Nums =6
limit1To10 Nums =7
limit1To10 Nums =8
limit1To10 Nums =9
limit1To10 Nums =10
Stream 還可以進行並行執行,下面介紹一下 並行執行的例子:
package iterable.java8;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
public class StreamDemo {
public static void main(String[] args) {
List<Integer> listData = new ArrayList<>();
for (int i = 0; i < 30; i++) {
listData.add(i);
}
// 1. 獲取流
Stream<Integer> streamListData = listData.parallelStream();
// 2. 過濾流 獲取集合中從1到10的數據集合
Stream<Integer> limit1To10 = streamListData.filter(p -> p >= 1 && p <= 10);
// 3. 打印信息
limit1To10.forEach(p -> System.out.println("limit1To10 Nums ="+p));
}
}
注意這裏 Stream<Integer> streamListData = listData.parallelStream();
輸出結果:
limit1To10 Nums =9
limit1To10 Nums =5
limit1To10 Nums =3
limit1To10 Nums =1
limit1To10 Nums =4
limit1To10 Nums =6
limit1To10 Nums =10
limit1To10 Nums =7
limit1To10 Nums =2
limit1To10 Nums =8
通過對比上面兩個例子的輸出結果,不難發現使用了 parallelStream 的操作,沒有按照順序輸出,因爲是並行運作,因此在處理大量集合時並行處理將非常有幫助。
5. Date Time API 加強對日期與時間的處理
直到 JDK 1.7,java.util.Date,java.util.Calendar 這兩個類是處理日期和時間的主要類,但是這兩個類的使用有以下問題:
- 線程不安全:java.util.Date 這個類是線程不安全;
- 處理麻煩:默認的開始日期從1900年,不支持國際化,不提供時區支持;
- 設計不合理:例如 java.util 和 java.sql 包中都有日期類,類名卻是一樣的。
JDK 1.8 中的新日期和時間API解決了的以上問題。
java.time 包中包含API中的核心類, 此外,還有其他四個包,使用概率比較少
- java.time.chrono:提供對不同的日曆系統的訪問
- java.time.temporal:包括底層框架和擴展特性
- java.time.format:提供用於打印和解析日期和時間的類
- java.time.zone:提供對時區及其規則的支持
6. 集合 API 改進
- Iterator類 forEachRemaining(Consumer action) 在所有元素都已處理完畢或該動作引發異常之前,對每個剩餘元素執行給定操作的默認方法,通過源碼中
action.accept(next());
其實是在對集合做數據處理的操作,這裏插入了一個指定的動作 action 。 - Collection類 removeIf(Predicate filter) 刪除滿足 filter 條件的集合中所有元素的方法。
- Collection類 spliterator() 該方法返回 Spliterator 實例,該實例可用於順序或並行遍歷元素,是爲了並行遍歷元素而設計的一個迭代器。
- Map類 replaceAll(),compute(),merge()方法。
- HashMap類 解決Hash衝突的Hash算法的改進,Entry 數據鏈的算法改進。
7. 併發 API 改進
ConcurrentHashMap
JDK1.8 中 針對 ConcurrentHashMap 做了一些改動,下面簡單介紹一下 JDK1.7 ConcurrentHashMap 的實現原理:
將整個hashmap分成幾個小的map,每個segment都是一個鎖;與hashtable相比,這麼設計的目的是對於put, remove等操作,可以減少併發衝突,對不屬於同一個片段的節點可以併發操作。
JDK1.8 做了如下改動:
- 取消 Segments 字段,直接採用
transient volatile HashEntry<K,V>[] table
保存數據,採用table數組元素作爲鎖,從而實現了對每一行數據進行加鎖,減少併發衝突的概率。 - 將原先table數組+單向鏈表的數據結構,變更爲table數組+單向鏈表+紅黑樹的結構。
Executors
Executors 類新增 newWorkStealingPool() 線程池:使所有可用處理器作爲目標,並行級別創建竊取線程池的方法。
”創建竊取“ 可以這麼簡單的理解:如果有三個線程 x , y , z ,如果 x 創建了3個任務, y 創建了 2 個任務, z 創建了 1 個任務, 假如,z 先 線程執行完任務後,它會主動的去 x 線程中竊取其他的任務進行執行(此時,x 線程尚未執行完成)。
8. Optional 類,解決空指針問題
Optional 類主要解決空指針異常 NullPointerException
Optional 類的使用示例
import java.util.Optional;
public class OptionalDemo {
public static void main(String[] args) {
String str = "OptionalDemo";
// 創建 Optional
Optional<String> opt = Optional.ofNullable(str);
// 獲取 Optional 中的值
String optValue = opt.get();
}
}
9. 新工具 新的編譯工具
Nashorn引擎:jjs
jjs 是一個基於標準 Nashorn 引擎的命令行工具,可以接受 js 源碼並執行。
編寫示例,jjsDemo.js 文件,內容如下:
function fun() {
return 1;
};
print( fun() + 1 );
在命令行中輸入執行命令 jjs jjsDemo.js
控制檯輸出結果是:
2
類依賴分析器:jdeps
jdeps 是一個命令行工具,它可以展示包層級和類層級的 Java 類依賴關係,以.class文件、目錄或者Jar文件爲輸入,把依賴關係輸出到控制檯。
例如直接在控制檯輸入:jdeps target
此時輸出在控制檯上的內容將會是,目前這個工作目錄直接的 Java 類依賴關係,如下:
iterable.java8 (target)
-> java.io
-> java.lang
-> java.lang.invoke
-> java.util
-> java.util.function
-> java.util.stream
結論
本文討論了 Java8 一些有趣的新功能和案例,Java 8 使得 Java 平臺發展又前進了一大步。
參考資料
Java 8 新特性
Java 8 Features with Examples (Java 8特性與示例)
New Features in Java 8(Java 8的新功能)
What’s New in JDK 8 (JDK 8的新增功能)
快過年了,今年最大的成就感,就是一直在朝着自己想要的方向前進。