0 引子
學妹:師兄師兄!我去面試被問到 JDK1.8 的新特性這個問題,不知道怎麼回答啊!哭了哭了,師兄能幫我總結一下 JDK1.8 的新特性嗎?
我:小意思!學妹你先坐下,且讓師兄爲你慢慢講解(嘻嘻)
1 前言
瞭解 JDK1.8 的新特性,無論是面試需要還是工作要求,對我們都是非常重要的,本文會介紹幾種 JDK1.8 的新特性,希望能夠對大家有所幫助。
2 JDK1.8 特性:Lambda 表達式
Lambda 表達式是一個匿名函數,Lambda 表達式沒有聲明的方法,也沒有訪問修飾符、返回值聲明和名字,用於幫助我們寫出更簡潔、更靈活的代碼。
Lambda 表達式建立在函數式接口之上,那什麼是函數式接口呢?只包含一個抽象方法的接口就被稱爲函數式接口,我們可以通過 Lambda 表達式來創建函數式接口的對象。
下面我們舉個栗子,相信大家經常使用排序功能吧,這裏我們使用 Comparator 接口對 Lambda 表達式的使用做一個測試。
我們先使用匿名內部類來實現 Comparator 接口。
public class Test {
public static void main(String[] args){
String[] array = {"apple","string","ss"};
//匿名內部類的使用
Arrays.sort(array,new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.length() - o2.length();
}
});
}
}
然後再使用 Lambda 表達式實現 Comparator 接口。
public class Test {
public static void main(String[] args){
String[] array = {"apple","string","ss"};
//使用 Lambda 表達式
Arrays.sort(array, (String s1, String s2) -> (s1.length() - s2.length()));
}
}
可以發現使用 Lambda 表達式更爲簡潔。
我們之前提到過 Lambda 表達式建立在函數式接口之上,爲什麼呢?其實是爲了保證唯一,我們上面的 Comparator 接口只有一個抽象方法,使用 Lambda 表達式可以代表那個方法,如果接口有多個抽象方法,誰知道 Lambda 表達式代表的是哪一個抽象方法呢?
3 JDK1.8 特性:函數式接口
我們在介紹 Lambda 表達式時其實已經講得很清楚了,函數式接口是隻有一個抽象函數的接口,在 JDK1.8 中提供了@FunctionalInterface 註解來檢查函數式接口的合法性。
下面我們介紹幾種基礎的函數式接口:
Consumer
消費者接口,有參無返回值,用於消費數據。
Consumer 接口中提供了 accept 抽象方法,accept 方法會接受一個變量,即使用該函數式接口時會提供數據,只需接受使用即可。
public class Test {
//使用消費型接口
public static void change(Consumer<String> con,String str) {
con.accept(str);
}
public static void main(String[] args){
change((str) -> System.out.println(str),"Hello World!");
}
}
Function
函數式接口,有參有返回值,提供轉換功能。
Function 接口中提供了 apply 抽象方法,apply 接受 T 類型數據並返回 R 類型數據。
public class Test {
//使用函數式接口
public static Integer change(Function<Integer, Integer> fun,Integer i) {
return fun.apply(i);
}
public static void main(String[] args){
Integer num = change((x) -> 2 * x,100);
System.out.println(num);
}
}
Predicate
斷言型接口,有參有返回值,其中會返回一個布爾類型的變量,提供斷言、判斷功能。
Predicate 接口中提供了 test 抽象方法,對傳入的數據進行判斷,返回 boolean 類型。
public class Test {
//使用斷言型接口
public static boolean change(Predicate<String> pre,String str) {
return pre.test(str);
}
public static void main(String[] args){
boolean res = change((str) -> str.equals("Hello"),"Hello");
System.out.println(res);
}
}
Supplier
供給型接口,無參有返回值,用於生產數據。
Supplier 接口中提供了 get 抽象方法,用於返回數據。
public class Test {
//使用供給型接口
public static String change(Supplier<String> sup) {
return sup.get();
}
public static void main(String[] args){
String str = change(() -> "Hello World!");
System.out.println(str);
}
}
函數式接口事實上是爲了更加方便地使用 Lambda 表達式,有了 JDK1.8 提供的函數式接口,我們不需要手動創建函數式接口,直接使用即可。
4 JDK1.8 特性:方法引用
我們經常使用 Lambda 表達式來創建匿名方法,但有時我們只是調用一個已經存在的方法而已。在 JDK1.8 中,可以通過方法引用來簡寫 Lambda 表達式中已經存在的方法。
方法引用是一種更簡潔易懂的 Lambda 表達式,其操作符爲雙冒號 :: 。方法引用是用來直接訪問類或者實例的已經存在的方法,它提供了一種引用而不執行方法的方式。
如果 Lambda 表達式僅僅調用一個已存在的方法而不做任何其它事,通過一個方法名字來引用這個已存在的方法也許會更加清晰,Java 8 的方法引用允許我們這樣操作。
下面我們舉個栗子,對一個 Integer 的封裝類進行排序。
Integer 的封裝類代碼如下:
class NewInteger{
private Integer num;
public NewInteger(Integer num) {
this.num = num;
}
public int getNum() {
return num;
}
public static int compare(NewInteger a,NewInteger b) {
return a.getNum() - b.getNum();
}
}
對其進行排序,我們可以使用匿名內部類寫法:
public class Test {
public static void main(String[] args){
NewInteger[] array = {new NewInteger(1),new NewInteger(12),new NewInteger(-1),new NewInteger(34)};
//匿名內部類寫法
Arrays.sort(array, new Comparator<NewInteger>() {
@Override
public int compare(NewInteger o1, NewInteger o2) {
return o1.getNum() - o2.getNum();
}
});
}
}
我們可以發現,Comparator 是一個函數式接口,故我們可以使用 Lambda 表達式,寫法如下:
public class Test {
public static void main(String[] args){
NewInteger[] array = {new NewInteger(1),new NewInteger(12),new NewInteger(-1),new NewInteger(34)};
//Lambda表達式寫法
Arrays.sort(array, (NewInteger a,NewInteger b) -> {
return a.getNum() - b.getNum();
});
}
}
其實,我們之前在 Integer 的封裝類中已經定義了一個比較方法,因此我們可以直接使用該比較方法:
public class Test {
public static void main(String[] args){
NewInteger[] array = {new NewInteger(1),new NewInteger(12),new NewInteger(-1),new NewInteger(34)};
//Lambda表達式寫法
Arrays.sort(array, (a ,b) -> NewInteger.compare(a, b));
}
}
由於 Lambda 表達式調用了一個已存在的方法,因此,我們可以使用方法引用來替代這個 Lambda 表達式。
public class Test {
public static void main(String[] args){
NewInteger[] array = {new NewInteger(1),new NewInteger(12),new NewInteger(-1),new NewInteger(34)};
//方法引用寫法
Arrays.sort(array, NewInteger::compare);
}
}
方法引用 NewInteger::compare 與 Lambda 表達式 (a ,b) -> NewInteger.compare(a, b) 是等價的。
方法引用的標準形式是 類名::方法名,一共有四種形式的方法引用,分別爲引用靜態方法,引用某個對象的實例方法,引用某個類型的任意對象的實例方法,引用構造方法。我們之前所舉例子就是一個靜態方法引用。
5 JDK1.8 特性:Stream API
由於文章篇幅關係,本文不準備詳細介紹 Stream 的各類 API,只會介紹一下 Stream 的概念,並舉一個使用 Stream 的小例子,關於 Stream 的各類 API,會在我的下一篇文章爲大家一一介紹。
Stream 是一個處理集合的關鍵抽象概念,可以對集合進行各種操作,Stream API 爲我們操作集合提供了強大的功能,同時操作簡單,容易上手。
Stream 一般有如下三個操作步驟:
- 創建 Stream:從一個數據源(集合、數組)中獲取流
- 中間操作:一個操作的中間鏈,對數據源的數據進行操作
- 終止操作:一個終止操作,執行中間操作鏈,併產生結果
注意,對流的操作完成後需要對其進行關閉。
如何理解 Stream?我們可以這麼想,集合的要點在於數據,流的要點在於計算。Stream 既不會存儲元素,也不會改變源對象,且會返回一個持有結果的新的 Stream,另外,Stream 的操作是延遲執行的,只有在需要結果時,纔會執行。
我們現在實現一個小功能,計算集合中大於 10 的元素數量,在 JDK1.8 之前,我們一般這樣實現:
public class Test {
public static void main(String[] args){
List<Integer> list = new ArrayList();
list.add(34);
list.add(7);
list.add(2);
list.add(66);
list.add(-3);
list.add(71);
int count = 0;
for (Integer integer : list) {
if(integer > 10) count++;
}
System.out.println("集合中大於10的元素數量:" + count );
}
}
如何使用 Stream API 的話,我們可以這樣寫代碼:
public class Test {
public static void main(String[] args){
List<Integer> list = new ArrayList();
list.add(34);
list.add(7);
list.add(2);
list.add(66);
list.add(-3);
list.add(71);
long count = list.stream().filter(i -> i > 10).count();
System.out.println("集合中大於10的元素數量:" + count );
}
}
使用 Stream API 一行代碼就解決了,是不是很簡單呢!
6 JDK1.8 特性:default 關鍵字
在 JDK1.8 之前,接口不能提供方法的實現,但在 JDK1.8 之後,我們可以爲方法提供默認實現方法和靜態方法,分別用關鍵字 default 和 static 修飾即可。
如果一個類既繼承父類又實現接口時,兩者方法名相同,類優先;如果實現兩個同名方法的接口,則實現類必須手動聲明默認實現哪個接口中的方法。
interface Car {
void begin();
//默認實現方法
default void play() {
System.out.println("正在開車");
}
//靜態方法
static void end() {
System.out.println("結束開車");
}
}
class Porsche implements Car {
@Override
public void begin() {
System.out.println("保時捷啓動");
}
}
public class Test {
public static void main(String[] args){
Car porsche = new Porsche();
porsche.begin();
porsche.play();
Car.end();
}
}
7 總結
JDK1.8 的幾個主要新特性我都簡單介紹了一下,還有一些別的特性,例如 Optional,Date API 等,由於文章篇幅問題,只能一筆帶過。面試能夠講到上面幾點,通過應該是沒問題的,但在工作中運用卻是遠遠不夠。冰凍三尺,非一日之寒,共勉!