1. 程式人生 > >JDK15就要來了,你卻還不知道JDK8的新特性!

JDK15就要來了,你卻還不知道JDK8的新特性!

> 微信搜「煙雨星空」,白嫖更多好文。 現在 Oracle 官方每隔半年就會出一個 JDK 新版本。按時間來算的話,這個月就要出 JDK15 了。然而,大部分公司還是在使用 JDK7 和 8 。 之前去我朋友家,竟然被嘲笑不會用 JDK8 。 不服氣的我,回來之後,當然是重點學習之啊。 ![](https://img2020.cnblogs.com/other/1714084/202009/1714084-20200907003922053-2025382979.png) 話不多說,本文目錄如下: **目錄:** * lambda 表示式 * 介面預設方法和靜態方法 * 函式式介面 * 方法引用 * Optional * Stream API * 日期時間新 API ## 一、lambda表示式 先看下 lambda 表示式是怎麼定義的: **lambda 表示式是一個匿名函式。** **lambda 表示式允許把一個函式作為引數進行傳遞。** 可能剛看到這兩句話時,不知道是什麼意思。那麼,對比一下 js 中的 setInterval 函式的用法,你就能找到一些感覺了。 ```javascript //每一秒執行一次匿名函式。(模擬時鐘) setInterval(function() { console.log("當前時間為:" + new Date()); }, 1000); ``` 如上,`function(){}`這段,就是一個匿名函式,並且可以把它作為引數傳遞給 setInterval 函式。 這是因為,在 js 中,函式是一等公民。 然而,在 Java 中,物件才是一等公民。但是,到了 JDK8 我們也可以通過 lambda 表示式表示同樣的效果。 lambda 表示式語法如下: ```java (引數1,引數2) -> { 方法體 } ``` 左邊指定了 lambda 表示式所需要的所有引數,右邊用來描述方法體。`->` 即為 lambda 運算子。 想一下,在之前我們通過匿名內部類的方式來啟動一個執行緒,是怎麼做的? ```java public class LambdaTest { @Test public void test(){ new Thread(new Runnable() { @Override public void run() { System.out.println("執行緒執行..."); } }).start(); } } ``` 現在,若把它改為用 lambda 表示式,則為, ```java public class LambdaTest { @Test public void test(){ // 一行搞定 new Thread(()->System.out.println("執行緒執行...")).start(); } } ``` 可以發現,明顯用 lambda 表示式,寫法更簡潔了。 其實,Lambda 表示式就是函數語言程式設計的體現。(什麼,你還不知道什麼是函數語言程式設計? 那還不趕快百度去。) **注意事項:** - 引數列表的資料型別會自動推斷。也就是說,如果匿名函式有引數列表的話,只需要寫引數名即可,不需要寫引數的型別。 - 如果引數列表為空,則左邊只需要寫小括號即可。 - 如果引數只有一個,則可以省略小括號,只寫引數的名稱即可。 - 如果方法體中只有一條執行語句,則可以省略右邊的大括號。若有返回值,則可以把 return 和大括號同時省略。 ## 二、介面預設方法和靜態方法 ### 介面預設方法 我們知道,在 Java 的介面中,只能定義方法名,不能實現方法體的,具體的實現需要子類去做。 但是,到了 JDK8 就不一樣了。在介面中,也可以通過 `default`關鍵字來實現方法體。 那麼,就有小夥伴疑惑了。好端端的,為什麼要加入這個奇怪的功能呢,它有什麼用? 當然是為了提高程式碼的重用性了。此外,介面的預設方法可以在不影響原來的繼承體系的情況下,進行功能的拓展,實現介面的向下相容。 我滴天,好抽象。那,就用例項來說明一下吧。 假設各種動物的繼承體系如下, ```java public interface Animal { //所有動物都需要吃東西,具體吃什麼,讓子類去實現 void eat(); } public class Bird implements Animal { @Override public void eat() { System.out.println("早起的鳥兒有蟲吃!"); } } public class Cat implements Animal { @Override public void eat() { System.out.println("小貓愛吃魚!"); } } ``` 現在,需要對 Animal介面拓展功能了。動物不能只會吃東西吧,它也許會奔跑,也許會飛行。那麼,我在介面中新增兩個方法, run 和 fly 就可以了吧。 這樣定義方法雖然是可以的,但是,問題就來了。介面中定義了方法,實現類就要實現它的所有方法。小貓會奔跑,但是不會飛啊。而小鳥會飛,你讓它在地上跑不是委屈人家嘛。 所以,這個設計不是太合理。 此時,就可以在介面中定義預設方法。子類不需要實現所有方法,可以按需實現,或者直接使用介面的預設方法。 因此,修改 Animal 介面如下,把 run 和 fly 定義為預設方法, ```java public interface Animal { //所有動物都需要吃東西,具體吃什麼,讓子類去實現 void eat(); default void run(){ System.out.println("我跑"); } default void fly(){ System.out.println("我飛"); } } public class Main { public static void main(String[] args) { Bird bird = new Bird(); bird.fly(); Cat cat = new Cat(); cat.run(); } } ``` 在 JDK8 的集合中,就對 Collection 介面進行了拓展,如增加預設方法 stream() 等。既增強了集合的一些功能,而且也能向下相容,不會對集合現有的繼承體系產生影響。 ![](https://img2020.cnblogs.com/other/1714084/202009/1714084-20200907003922287-1305123361.png) ### 介面靜態方法 另外,在介面中也可以定義靜態方法。這樣,就可以直接通過介面名呼叫靜態方法。(這也很正常,介面本來就不能例項化) 需要注意的是,不能通過實現類的物件去呼叫介面的靜態方法。 ```java public interface MyStaticInterface { static void method(){ System.out.println("這是介面的靜態方法"); } } public class MyStaticInterfaceImpl implements MyStaticInterface { public static void main(String[] args) { //直接通過介面名呼叫靜態方法,不能通過實現類的物件呼叫 MyStaticInterface.method(); } } ``` ## 三、函式式介面 如果一個介面中只有一個抽象方法,則稱其為函式式介面。可以使用 `@FunctionalInterface` 註解來檢測一個介面是否為函式式介面。 JDK提供了常見的最簡單的四種函式式介面:(必須掌握哦) -