JDK15就要來了,你卻還不知道JDK8的新特性!
阿新 • • 發佈:2020-09-07
> 微信搜「煙雨星空」,白嫖更多好文。
現在 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提供了常見的最簡單的四種函式式介面:(必須掌握哦)
-