Java 8 (又稱(chēng)為 jdk 1.8) 是 Java 語(yǔ)言開(kāi)發(fā)的一個(gè)主要版本。 Oracle 公司于 2014 年 3 月 18 日發(fā)布 Java 8 ,它支持函數(shù)式編程,新的 JavaScript 引擎,新的日期 API,新的 Stream API 等。
一、新特性
Java 8 新增了非常多的特性,我們主要討論以下幾個(gè):
1. Lambda 表達(dá)式
Lambda 表達(dá)式,也可稱(chēng)為閉包,它是推動(dòng) Java 8 發(fā)布的最重要新特性。Lambda 允許把函數(shù)作為一個(gè)方法的參數(shù)(函數(shù)作為參數(shù)傳遞進(jìn)方法中)。使用 Lambda 表達(dá)式可以使代碼變的更加簡(jiǎn)潔緊湊。
Lambda 表達(dá)式的語(yǔ)法格式如下:
(parameters) -> expression
或
(parameters) ->{ statements; }
以下是 Lambda 表達(dá)式的重要特征:
可選類(lèi)型聲明:不需要聲明參數(shù)類(lèi)型,編譯器可以統(tǒng)一識(shí)別參數(shù)值。
可選的參數(shù)圓括號(hào):一個(gè)參數(shù)無(wú)需定義圓括號(hào),但多個(gè)參數(shù)需要定義圓括號(hào)。
可選的大括號(hào):如果主體包含了一個(gè)語(yǔ)句,就不需要使用大括號(hào)。
可選的返回關(guān)鍵字:如果主體只有一個(gè)表達(dá)式返回值則編譯器會(huì)自動(dòng)返回值,大括號(hào)需要指定明表達(dá)式返回了一個(gè)數(shù)值。
Lambda 表達(dá)式的簡(jiǎn)單例子:
// 1. 不需要參數(shù),返回值為 5
() -> 5
// 2. 接收一個(gè)參數(shù)(數(shù)字類(lèi)型),返回其2倍的值
x -> 2 * x
// 3. 接受2個(gè)參數(shù)(數(shù)字),并返回他們的差值
(x, y) -> x – y
// 4. 接收2個(gè)int型整數(shù),返回他們的和
(int x, int y) -> x + y
// 5. 接受一個(gè) string 對(duì)象,并在控制臺(tái)打印,不返回任何值(看起來(lái)像是返回void)
(String s) -> System.out.print(s)
2. 方法引用
方法引用提供了非常有用的語(yǔ)法,可以直接引用已有 Java 類(lèi)或?qū)ο螅▽?shí)例)的方法或構(gòu)造器。與 lambda 聯(lián)合使用,方法引用可以使語(yǔ)言的構(gòu)造更緊湊簡(jiǎn)潔,減少冗余代碼。
方法引用通過(guò)方法的名字來(lái)指向一個(gè)方法。方法引用使用一對(duì)冒號(hào) :: 。
下面,我們?cè)?Car 類(lèi)中定義了 4 個(gè)方法作為例子來(lái)區(qū)分 Java 中 4 種不同方法的引用。
package com.runoob.main;
@FunctionalInterface
public interface Supplier<T> {
T get();
}
class Car {
//Supplier是jdk1.8的接口,這里和lamda一起使用了
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());
}
}
- 構(gòu)造器引用:它的語(yǔ)法是 Class::new,或者更一般的 Class< T >::new實(shí)例如下:
final Car car = Car.create( Car::new );
final List< Car > cars = Arrays.asList( car );
- 靜態(tài)方法引用:它的語(yǔ)法是 Class::static_method,實(shí)例如下:
cars.forEach( Car::collide );
- 特定類(lèi)的任意對(duì)象的方法引用:它的語(yǔ)法是 Class::method 實(shí)例如下:
cars.forEach( Car::repair );
- 特定對(duì)象的方法引用:它的語(yǔ)法是 instance::method 實(shí)例如下:
final Car police = Car.create( Car::new );
cars.forEach( police::follow );
3. 函數(shù)式接口
函數(shù)式接口 (Functional Interface) 就是一個(gè)有且僅有一個(gè)抽象方法,但是可以有多個(gè)非抽象方法的接口。函數(shù)式接口可以被隱式轉(zhuǎn)換為 lambda 表達(dá)式。函數(shù)式接口可以讓現(xiàn)有的函數(shù)友好地支持 lambda。
4. 默認(rèn)方法
默認(rèn)方法就是一個(gè)在接口里面有了一個(gè)實(shí)現(xiàn)的方法。
5. 新工具
新的編譯工具,如:Nashorn 引擎 jjs、 類(lèi)依賴(lài)分析器 jdeps。
6. Stream API
新添加的 Stream API(java.util.stream) 把真正的函數(shù)式編程風(fēng)格引入到 Java 中。Java 8 API 添加了一個(gè)新的抽象稱(chēng)為流 Stream,可以讓你以一種聲明的方式處理數(shù)據(jù)。Stream 使用一種類(lèi)似用 SQL 語(yǔ)句從數(shù)據(jù)庫(kù)查詢(xún)數(shù)據(jù)的直觀方式來(lái)提供一種對(duì) Java 集合運(yùn)算和表達(dá)的高階抽象。
這種風(fēng)格將要處理的元素集合看作一種流, 流在管道中傳輸, 并且可以在管道的節(jié)點(diǎn)上進(jìn)行處理, 比如篩選, 排序,聚合等。元素流在管道中經(jīng)過(guò)中間操作(intermediate operation)的處理,最后由最終操作(terminal operation)得到前面處理的結(jié)果。
+--------------------+ +------+ +------+ +---+ +-------+
| stream of elements +-----> |filter+-> |sorted+-> |map+-> |collect|
+--------------------+ +------+ +------+ +---+ +-------+
以上的流程轉(zhuǎn)換為 Java 代碼為:
List<Integer> transactionsIds =
widgets.stream()
.filter(b -> b.getColor() == RED)
.sorted((x,y) -> x.getWeight() - y.getWeight())
.mapToInt(Widget::getWeight)
.sum();
在 Java 8 中, 集合接口有兩個(gè)方法來(lái)生成流:
stream() ? 為集合創(chuàng)建串行流。
parallelStream() ? 為集合創(chuàng)建并行流。
List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
List<String> filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList());
7. Optional 類(lèi)
Optional 類(lèi)是一個(gè)可以為 null 的容器對(duì)象。如果值存在則 isPresent() 方法會(huì)返回 true,調(diào)用 get() 方法會(huì)返回該對(duì)象。
Optional 是個(gè)容器:它可以保存類(lèi)型 T 的值,或者僅僅保存 null。Optional 提供很多有用的方法,這樣我們就不用顯式進(jìn)行空值檢測(cè)。Optional 類(lèi)的引入很好的解決空指針異常。
8. Nashorn, JavaScript 引擎
Java 8 提供了一個(gè)新的 Nashorn javascript 引擎,它允許我們?cè)?JVM 上運(yùn)行特定的 javascript 應(yīng)用。
9. Date Time API
10. Base 64
在 Java 8 中,Base64 編碼已經(jīng)成為 Java 類(lèi)庫(kù)的標(biāo)準(zhǔn)。Java 8 內(nèi)置了 Base64 編碼的編碼器和解碼器。
Base64 工具類(lèi)提供了一套靜態(tài)方法獲取下面三種 BASE64 編解碼器:
基本:輸出被映射到一組字符 A-Za-z0-9+/,編碼不添加任何行標(biāo),輸出的解碼僅支持 A-Za-z0-9+/。
URL:輸出映射到一組字符 A-Za-z0-9+_,輸出是 URL 和文件。
MIME:輸出隱射到 MIME 友好格式。輸出每行不超過(guò) 76 字符,并且使用 '\r' 并跟隨 '\n' 作為分割。編碼輸出最后沒(méi)有行分割。
二、編程風(fēng)格
Java 8 希望有自己的編程風(fēng)格,并與 Java 7 區(qū)別開(kāi),以下實(shí)例展示了 Java 7 和 Java 8 的編程格式:
Java8Tester.java 文件代碼:
import java.util.Collections;
import java.util.List;
import java.util.ArrayList;
import java.util.Comparator;
public class Java8Tester {
public static void main(String args[]){
List<String> names1 = new ArrayList<String>();
names1.add("Google ");
names1.add("Runoob ");
names1.add("Taobao ");
names1.add("Baidu ");
names1.add("Sina ");
List<String> names2 = new ArrayList<String>();
names2.add("Google ");
names2.add("Runoob ");
names2.add("Taobao ");
names2.add("Baidu ");
names2.add("Sina ");
Java8Tester tester = new Java8Tester();
System.out.println("使用 Java 7 語(yǔ)法: ");
tester.sortUsingJava7(names1);
System.out.println(names1);
System.out.println("使用 Java 8 語(yǔ)法: ");
tester.sortUsingJava8(names2);
System.out.println(names2);
}
// 使用 java 7 排序
private void sortUsingJava7(List<String> names){
Collections.sort(names, new Comparator<String>() {
@Override
public int compare(String s1, String s2) {
return s1.compareTo(s2);
}
});
}
// 使用 java 8 排序
private void sortUsingJava8(List<String> names){
Collections.sort(names, (s1, s2) -> s1.compareTo(s2));
}
}
執(zhí)行以上腳本,輸出結(jié)果為:
$ javac Java8Tester.java
$ java Java8Tester
使用 Java 7 語(yǔ)法:
[Baidu , Google , Runoob , Sina , Taobao ]
使用 Java 8 語(yǔ)法:
[Baidu , Google , Runoob , Sina , Taobao ]