Java8中使用Optional處理null對象

系統環境:

  • Java JDK 版本:1.8

參考地址:

  • Oracle JDK API 參考文檔

https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html

  • 菜鳥教程-Java 8 Optional 類

https://www.runoob.com/java/java8-optional-class.html

1.Optional簡介

  Optional 是一個容器對象,可以存儲對象、字符串等值,當然也可以存儲 null 值。Optional 提供很多有用的方法,能幫助我們將 Java 中的對象等一些值存入其中,這樣我們就不用顯式進行空值檢測,使我們能夠用少量的代碼完成複雜的流程。

比如它提供了:

  • of() 方法,可以將值存入 Optional 容器中,如果存入的值是 null 則拋異常。

  • ofNullable() 方法,可以將值存入 Optional 容器中,即使值是 null 也不會拋異常。

  • get() 方法,可以獲取容器中的值,如果值爲 null 則拋出異常。

  • getElse() 方法,可以獲取容器中的值,如果值爲 null 則返回設置的默認值。

  • isPresent() 方法,該方法可以判斷存入的值是否爲空。

  • …等等一些其它常用方法,下面會進行介紹。

可以說,使用 Optional 可以幫助我們解決業務中,減少值動不動就拋出空指針異常問題,也減少 null 值的判斷,提高代碼可讀性等,這裏我們介紹下,如果使用這個 Optional 類。

 

2.Optional類描述

  • Optional 類所在包: java.util.Optional

  • Optional 類聲明: public final class Optional extends Object

  • Optional 類方法: public Optional filter(Predicate<? super T> predicate)

  簡單來說,Opitonal類就是Java提供的爲了解決大家平時判斷對象是否爲空用會用 null!=obj 這樣的方式存在的判斷,從而令人頭疼導致NPE(Null Pointer Exception 空指針異常),同時Optional的存在可以讓代碼更加簡單,可讀性跟高,代碼寫起來更高效.

常規判斷:

//對象 人
//屬性有 name,age
Person person=new Person();
if (null==person){
   return "person爲null";
}
return person;

使用Optional:

//對象 人
//屬性有 name,age
Person person=new Person();
return Optional.ofNullable(person).orElse("person爲null");

3、Optional 常用方法及使用示例

1、靜態方法 Optional.of()

  • 方法作用: 爲指定的值創建一個指定非 null 值的 Optional。

  • 方法描述: of 方法通過工廠方法創建 Optional 實例,需要注意的是傳入的參數不能爲 null,否則拋出 NullPointerException。

  • 返回類型: Optional

  • 示例代碼:

調用兩個 Optional.of() 方法,一個傳入正常參數,另一個傳入 null 參數:

public static void main(String[] args) {
    // 傳入正常值,正常返回一個 Optional 對象
    Optional<String> optional1 = Optional.of("mydlq");
    
    // 傳入參數爲 null,拋出 NullPointerException.
    Optional optional2 = Optional.of(null);
}

運行代碼,可以觀察到控制檯輸出內容如下:

Exception in thread "main" java.lang.NullPointerException
    at java.util.Objects.requireNonNull(Objects.java:203)
    at java.util.Optional.<init>(Optional.java:96)
    at java.util.Optional.of(Optional.java:108)
    at club.mydlq.OptionalExample.main(OptionalExample.java:12)

可以看到傳入正常參數正常返回 Optional 對象,傳入 null 參數返回 NullPointerException 異常。

2、靜態方法 Optional.ofNullable()

  • 方法作用: 爲指定的值創建一個 Optional 對象,如果指定的參數爲 null,不拋出異常,直接則返回一個空的 Optional 對象。

  • 方法描述: ofNullable 方法是和 of 方式一樣,都是用於創建 Optional 對象,只是傳入的參數 null 時,會返回一個空的 Optional 對象,而不會拋出 NullPointerException 異常。

  • 返回類型: Optional

  • 示例代碼:

調用 Optional.ofNullable() 方法,傳入 null 參數:

public static void main(String[] args) {
    // 傳入正常值,正常返回一個 Optional 對象
    Optional<String> optional1 = Optional.ofNullable("mydlq");
    
    // 傳入 null 參數,正常返回 Optional 對象
    Optional optional2 = Optional.ofNullable(null);
}

運行代碼,可以觀察到正常傳入值和傳入 null 值時,都沒有拋出異常。

3、對象方法 isPresent()

  • 方法作用: 如果值存在則方法會返回 true,否則返回 false。

  • 方法描述: 該方法其實就是用於判斷創建 Optional 時傳入參數的值是否爲空,實現代碼就簡單一行,即 value != null 所以如果不爲空則返回 true,否則返回 false。

  • 返回類型: boolean

  • 示例代碼:

public static void main(String[] args) {
    // 傳入正常值,正常返回一個 Optional 對象,並使用 isPresent 方法
    Optional optional1 = Optional.ofNullable("mydlq");
    System.out.println("傳入正常值返回:" + optional1.isPresent());

    // 傳入參數爲 null 生成一個 Optional 對象,並使用 isPresent 方法
    Optional optional2 = Optional.ofNullable(null);
    System.out.println("傳入 null 值返回:" + optional2.isPresent());
}

運行代碼,可以觀察到控制檯輸出內容如下:

傳入正常值返回:true
傳入 null 值返回:false

可以看到傳入正常參數時調用 Optional 對象的 isPresent 方法時返回 true,傳入 null 參數返回 false。

4、對象方法 get()

  • 方法作用: 如果 Optional 有值則將其返回,否則拋出 NoSuchElementException 異常。

  • 方法描述: get 方法內部實現其實就是判斷 Otpional 對象中的 value 屬性是否爲 null,如果是就拋出 NoSuchElementException 異常,否則返回這個 value 值。

  • 返回類型: T

  • 示例代碼:

public static void main(String[] args) {
    // 傳入正常值,正常返回一個 Optional 對象,並使用 get 方法獲取值
    Optional optional1 = Optional.ofNullable("mydlq");
    System.out.println(optional1.get());

    // 傳入參數爲 null 生成一個 Optional 對象,並使用 get 方法獲取值
    Optional optional2 = Optional.ofNullable(null);
    System.out.println(optional2.get());
}

運行代碼,可以觀察到控制檯輸出內容如下:

傳入正常參數:mydlq
Exception in thread "main" java.util.NoSuchElementException: No value present
    at java.util.Optional.get(Optional.java:135)
    at club.mydlq.OptionalExample.main(OptionalExample.java:14)

可以觀察到傳入正常值的 Optional 調用 get 方法正常輸出值,通過空的 optional 對象使用 get 方法獲取值時,拋出 NoSuchElementException 異常:

5、對象方法 ifPresent()

  • 方法作用: 如果值存在則使用該值調用 consumer , 否則不做任何事情。

  • 方法描述: 該方法 ifPresent(Consumer<? super T> consumer) 中參數接收的是 Consumer 類,它包含一個接口方法 accept(),該方法能夠對傳入的值進行處理,但不會返回結果。這裏傳入參數可以傳入 Lamdda 表達式或 Consumer 對象及實現 Consumer 接口的類的對象。

  • 返回類型: void

  • 示例代碼:

public static void main(String[] args) {
    // 創建 Optional 對象,然後調用 Optional 對象的 ifPresent 方法,傳入 Lambda 表達式
    Optional optional1 = Optional.ofNullable("mydlq1");
    optional1.ifPresent((value) -> System.out.println("Optional 的值爲:" + value));

    // 創建 Optional 對象,調用 Optional 對象的 ifPresent 方法,傳入實現 Consumer 匿名內部類
    Optional optional2 = Optional.ofNullable("mydlq2");
    Consumer<String> consumer = new Consumer() {
        @Override
        public void accept(Object value) {
            System.out.println("Optional 的值爲:" + value);
        }
    };
    optional2.ifPresent(consumer);
}

運行代碼,可以觀察到控制檯輸出內容如下:

Optional 的值爲:mydlq1
Optional 的值爲:mydlq2

可以觀察到,調用 ifPresent 使用 lambda 或者內部匿名類方法,都是爲了再執行 Optional 對象的 ifPresent 方法時,執行一段代碼邏輯。

6、對象方法 orElse()

  • 方法作用: 如果該值存在就直接返回, 否則返回指定的其它值。

  • 方法描述: orElse 方法實現很簡單,就是使用三目表達式對傳入的參數值進行 null 驗證,即 value != null ? value : other; 如果爲 null 則返回 true,否則返回 false。

  • 返回類型: T

  • 示例代碼:

public static void main(String[] args) {
    // 傳入正常參數,獲取一個 Optional 對象,並使用 orElse 方法設置默認值
    Optional optional1 = Optional.ofNullable("mydlq");
    Object object1 = optional1.orElse("默認值");
    System.out.println("如果值不爲空:"+object1);

    // 傳入 null 參數,獲取一個 Optional 對象,並使用 orElse 方法設置默認值
    Optional optional2 = Optional.ofNullable(null);
    Object object2 = optional2.orElse("默認值");
    System.out.println("如果值爲空:"+object2);
}

運行代碼,可以觀察到控制檯輸出內容如下:

如果值不爲空:mydlq
如果值爲空:默認值

可以觀察到,如果 Optional 的值爲空,則返回 orElse() 方法設置的默認值,否則返回 Optional 中的值。

7、對象方法 orElseGet()

  • 方法作用: 如果該值存在就返回值,否則觸發 other,並返回 other 調用的結果。

  • 方法描述: orElseGet 方法和 orElse 方法類似,都是在 Optional 值爲空時,返回一個默認操作,只不過 orElse 返回的是默認值,而 orElseGet 是執行 lambda 表達式,然後返回 lambda 表達式執行後的結果。

  • 返回類型: T

  • 示例代碼:

public static void main(String[] args) {
    // 傳入正常參數,獲取一個 Optional 對象,並使用 orElse 方法設置默認值
    Optional optional1 = Optional.ofNullable("mydlq");
    Object object1 = optional1.orElseGet(() -> {
        String defaultVal = "執行邏輯和生成的默認值";
        return defaultVal;
    });
    System.out.println("輸出的值爲:"+object1);

    // 傳入 null 參數,獲取一個 Optional 對象,並使用 orElse 方法設置默認值
    Optional optional2 = Optional.ofNullable(null);
    Object object2 = optional2.orElseGet(() -> {
        String defaultVal = "執行邏輯和生成的默認值";
        return defaultVal;
    });
    System.out.println("輸出的值爲:"+object2);
}

運行代碼,可以觀察到控制檯輸出內容如下:

輸出的值爲:mydlq
輸出的值爲:執行邏輯和生成的默認值

可也觀察到,當 Optional 值爲不爲空時正常返回帶值的 Optional,如果 Optional 爲空則返回 orElseGet 方法中 lambda 表達式執行後生成的值。

8、對象方法 orElseThrow()

  • 方法作用: 如果 Optional 存在該值,返回包含的值,否則拋出由 Supplier 繼承的異常。

  • 方法描述: orElseThrow 方法其實就是判斷創建 Optional 時傳入的參數是否爲 null,如果是非 null 則返回傳入的值,否則拋出 異常。

  • 返回類型:   T

  • 示例代碼:

public static void main(String[] args) {
    // 傳入正常參數,獲取一個 Optional 對象,並使用 orElseThrow 方法
    try {
        Optional optional1 = Optional.ofNullable("mydlq");
        Object object1 = optional1.orElseThrow(() -> {
                    System.out.println("執行邏輯,然後拋出異常");
                    return new RuntimeException("拋出異常");
                }
        );
        System.out.println("輸出的值爲:" + object1);
    } catch (Throwable throwable) {
        throwable.printStackTrace();
    }
    
    // 傳入 null 參數,獲取一個 Optional 對象,並使用 orElseThrow 方法
    try {
        Optional optional2 = Optional.ofNullable(null);
        Object object2 = optional2.orElseThrow(() -> {
                    System.out.println("執行邏輯,然後拋出異常");
                    return new RuntimeException("拋出異常");
                }
        );
        System.out.println("輸出的值爲:" + object2);
    } catch (Throwable throwable) {
        throwable.printStackTrace();
    }
}

 運行代碼,可以觀察到控制檯輸出內容如下:

值爲不爲空輸出的值:mydlq
執行邏輯,然後拋出異常
java.lang.RuntimeException: 拋出異常
    at club.mydlq.OptionalExample.lambda$main$1(OptionalExample.java:25)
    at java.util.Optional.orElseThrow(Optional.java:290)
    at club.mydlq.OptionalExample.main(OptionalExample.java:23)

可以觀察到,當創建 Optional 時如果傳入的參數爲空則執行 Lambda 表達式代碼邏輯後拋出異常信息,否則返回傳入的參數值。

9、對象方法 map()

  • 方法作用: 如果有值,則對其執行調用映射函數得到返回值。如果返回值不爲 null,則創建包含映射返回值的 Optional 作爲 map 方法返回值,否則返回空 Optional。

  • 方法描述: map 方法主要用於獲取某個對象中的某個屬性值的 Optional 對象時使用。map 方法調用時,首先驗證傳入的映射函數是否爲空,如果爲空則拋出異常。然後,再檢測 Optional 的 value 是否爲空,如果是,則返回一個空 value 的 Optional 對象。如果傳入的映射函數和 Optinal 的 value 都不爲空,則返回一個帶 value 對象屬性的 Optional 對象。

  • 返回類型: Optional<U>

  • 示例代碼:

示例1: 創建 Map 集合,存儲一些鍵值對信息,通過 Optional 操作 Map 獲取值,然後觀察:

public static void main(String[] args) {
    // 創建 map 對象
    Map<String, String> userMap = new HashMap<>();
    userMap.put("name1", "mydlq");
    userMap.put("name2", null);

    // 傳入 Map 對象參數,獲取一個 Optional 對象,獲取 name1 屬性
    Optional<String> optional1 = Optional.of(userMap).map(value -> value.get("name1"));

    // 傳入 Map 對象參數,獲取一個 Optional 對象,獲取 name2 屬性
    Optional<String> optional2 = Optional.of(userMap).map(value -> value.get("name2"));

    // 獲取 Optional 的值
    System.out.println("獲取的 name1 的值:" + optional1.orElse("默認值"));
    System.out.println("獲取的 name2 的值:" + optional2.orElse("默認值"));
}

運行代碼,可以觀察到控制檯輸出內容如下:

獲取的 Optional 的值:mydlq
獲取的 Optional 的值:默認值

示例2: 創建一個用戶類,使用 Optional 操作用戶對象,獲取其 name 參數,結合 Optional 的 map 方法獲取值,進行觀察:

public class User {

    private String name;
    
    public User(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

使用 Optional 的 map 方法對值處理:

public static void main(String[] args) {
    // 創建一個對象,設置姓名屬性而不設置性別,這時候性別爲 null
    User user1 = new User("測試名稱");
    User user2 = new User();

    // 使用 Optional 存儲 User 對象
    Optional<User> optional1 = Optional.ofNullable(user1);
    Optional<User> optional2 = Optional.ofNullable(user2);

    // 獲取對象的 name 屬性值
    String name1 = optional1.map(User::getName).orElse("未填寫");
    String name2 = optional2.map(User::getName).orElse("未填寫");

    // 輸出結果
    System.out.println("獲取的名稱:" + name1);
    System.out.println("獲取的名稱:" + name2);
}

運行代碼,可以觀察到控制檯輸出內容如下:

獲取的名稱:測試名稱
獲取的名稱:未填寫

總結:

通過上面兩個示例觀察到,通過 Optional 對象的 map 方法能夠獲取映射對象中的屬,創建 Optional 對象,並以此屬性充當 Optional 的值,結合 orElse 方法,如果獲取的屬性的值爲空,則設置個默認值。

10、對象方法 flatMap()

  • 方法作用: 如果值存在,返回基於 Optional 包含的映射方法的值,否則返回一個空的 Optional。

  • 方法描述: flatMap 方法和 map 方法類似,唯一的不同點就是 map 方法會對返回的值進行 Optional 封裝,而 flatMap 不會,它需要手動執行 Optional.of 或 Optional.ofNullable 方法對 Optional 值進行封裝。

  • 返回類型: Optional<U>

  • 示例代碼:

public static void main(String[] args) {
    // 創建 map 對象
    Map<String, String> userMap = new HashMap<>();
    userMap.put("name", "mydlq");
    userMap.put("sex", "男");

    // 傳入 Map 對象參數,獲取一個 Optional 對象
    Optional<Map<String, String>> optional1 = Optional.of(userMap);

    // 使用 Optional 的 flatMap 方法,獲取 Map 中的 name 屬性
    // 然後通過獲取的值手動創建一個新的 Optional 對象
    Optional optional2 = optional1.flatMap(value -> Optional.ofNullable(value.get("name")));

    // 獲取 Optional 的 value
    System.out.println("獲取的 Optional 的值:" + optional2.get());
}

運行代碼,可以觀察到控制檯輸出內容如下:

獲取的 Optional 的值:mydlq

根據結果觀察,可以看到 flatMap 和 map 方法沒有什麼區別,但是仔細看,代碼中調用 flatMap 後,需要手動執行 of 或 ofNullable 方法創建了 Optional 對象。

11、對象方法 filter()

  • 方法作用: 如果有值並且滿足斷言條件返回包含該值的 Optional,否則返回空 Optional。

  • 方法描述: filter 方法通過傳入的限定條件對 Optional 實例的值進行過濾,如果 Optional 值不爲空且滿足限定條件就返回包含值的 Optional,否則返回空的 Optional。這裏設置的限定條件需要使用實現了 Predicate 接口的 lambda 表達式來進行配置。

  • 返回類型: Optional<T>

  • 示例代碼:

public static void main(String[] args) {
    // 創建一個測試的 Optional 對象
    Optional<String> optional = Optional.ofNullable("mydlq");
    // 調用 Optional 的 filter 方法,設置一個滿足的條件,然後觀察獲取的 Optional 對象值是否爲空
    Optional optional1 =optional.filter((value) -> value.length() > 2);
    System.out.println("Optional 的值不爲空::" + optional.isPresent());

    // 調用 Optional 的 filter 方法,設置一個不滿足的條件,然後觀察獲取的 Optional 對象值是否爲空
    Optional optional2 =optional.filter((value) -> value.length() <2);
    System.out.println("Optional 的值不爲空::" + optional2.isPresent());
}

運行代碼,可以觀察到控制檯輸出內容如下:

Optional 的值不爲空:true
Optional 的值不爲空:false

根據結果可以觀察到,可以通過 filter 設置一個條件來判斷 Optional 的值,如果滿足條件就返回帶值的 Optional,否則返回空的 Optional。

4、Optional 常用示例組合

  在介紹一欄中已經說過 Optional 是個容器,它可用保存類型的 T 的值,即使 T 爲 null 也可以使用 Optional 存儲,這樣我就不用顯示進行空值檢測,防止空指針異常。

  上面也介紹了 Optional 的各種方法,在實際使用中這些方法常常組合使用。且很多方法也常與 Lambda 表達式結合,獲取我們想要的結果的值。

下面是常用的示例,可以作爲參考:

對集合中的對象屬性進行過濾

創建一個 User 對象實體類,裏面包含 name 屬性:

public class User {
    private String name;

    public User(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

創建一個使用 main 方法的類,創建幾個 User 對象且設置不同的值,有的對象爲 null 有的屬性不設置,然後通過 Optional 獲取 name 屬性值加入集合,進行測試:

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

public class OptionalExample {

    /**
     * 測試的 main 方法
     */
    public static void main(String[] args) {
        // 創建一個測試的用戶集合
        List<User> userList = new ArrayList<>();

        // 創建幾個測試用戶
        User user1 = new User("abc");
        User user2 = new User("efg");
        User user3 = null;

        // 將用戶加入集合
        userList.add(user1);
        userList.add(user2);
        userList.add(user3);

        // 創建用於存儲姓名的集合
        List<String> nameList = new ArrayList();
        // 循環用戶列表獲取用戶信息,值獲取不爲空且用戶以 a 開頭的姓名,
        // 如果不符合條件就設置默認值,最後將符合條件的用戶姓名加入姓名集合
        for (User user : userList) {
            nameList.add(Optional.ofNullable(user).map(User::getName).filter(value -> value.startsWith("a")).orElse("未填寫"));
        }

        // 輸出名字集合中的值
        System.out.println("通過 Optional 過濾的集合輸出:");
        nameList.stream().forEach(System.out::println);
    }

}

輸出運行結果:

通過 Optional 過濾的集合輸出:
abc
未填寫
未填寫

通過上面,可以觀察到,使用 Optional 有時候可以很方便的過濾一些屬性,而且它的方法可以通過鏈式調用,方法間相互組合使用,使我們用少量的代碼就能完成複雜的邏輯。

5.相似方法進行對比分析

  可能小夥伴看到這,沒用用過的話會覺得orElse()和orElseGet()還有orElseThrow()很相似,map()和flatMap()好相似,哈哈哈不用着急,都是從這一步過來的,我再給大家總結一下不同方法的異同點

orElse()和orElseGet()和orElseThrow()的異同點

方法效果類似,如果對象不爲空,則返回對象,如果爲空,則返回方法體中的對應參數,所以可以看出這三個方法體中參數是不一樣的

  • orElse(T 對象)

  • orElseGet(Supplier < T >對象)

  • orElseThrow(異常)

map()和orElseGet的異同點

  • 方法效果類似,對方法參數進行二次包裝,並返回,入參不同

  • map(function函數)

  • flatmap(Optional< function >函數)

具體要怎麼用,要根據業務場景以及代碼規範來定義,下面可以簡單看一下我在實戰中怎用使用神奇的Optional

實戰場景再現

場景1:

在service層中查詢一個對象,返回之後判斷是否爲空並做處理

//查詢一個對象
Member member = memberService.selectByIdNo(request.getCertificateNo());
//使用ofNullable加orElseThrow做判斷和操作
Optional.ofNullable(member).orElseThrow(() -> new ServiceException("沒有查詢的相關數據"));

場景2:

我們可以在dao接口層中定義返回值時就加上Optional 例如:我使用的是jpa,其他也同理

public interface LocationRepository extends JpaRepository<Location, String> {
Optional<Location> findLocationById(String id);
}

然在是Service中

public TerminalVO findById(String id) {
//這個方法在dao層也是用了Optional包裝了
        Optional<Terminal> terminalOptional = terminalRepository.findById(id);
        //直接使用isPresent()判斷是否爲空
        if (terminalOptional.isPresent()) {
        //使用get()方法獲取對象值
            Terminal terminal = terminalOptional.get();
            //在實戰中,我們已經免去了用set去賦值的繁瑣,直接用BeanCopy去賦值
            TerminalVO terminalVO = BeanCopyUtils.copyBean(terminal, TerminalVO.class);
            //調用dao層方法返回包裝後的對象
            Optional<Location> location = locationRepository.findLocationById(terminal.getLocationId());
            if (location.isPresent()) {
                terminalVO.setFullName(location.get().getFullName());
            }
            return terminalVO;
        }
        //不要忘記拋出異常
        throw new ServiceException("該終端不存在");
    }

6.Optional使用注意事項

Optional真麼好用,真的可以完全替代if判斷嗎?

我想這肯定是大家使用完之後Optional之後可能會產生的想法,答案是否定的,舉一個最簡單的栗子:

如果我只想判斷對象的某一個變量是否爲空並且做出判斷呢?

Person person=new Person();
person.setName("");
persion.setAge(2);
//普通判斷
if(StringUtils.isNotBlank(person.getName())){
  //名稱不爲空執行代碼塊
}
//使用Optional做判斷
Optional.ofNullable(person).map(p -> p.getName()).orElse("name爲空");

  我覺得這個例子就能很好的說明這個問題,只是一個很簡單判斷,如果用了Optional我們還需要考慮包裝值,考慮代碼書寫,考慮方法調用,雖然只有一行,但是可讀性並不好,如果別的程序員去讀,我覺得肯定沒有if看的明顯。

7.jdk1.9對Optional優化

首先增加了三個方法:or()ifPresentOrElse() 和 stream()

  • or() 與orElse等方法相似,如果對象不爲空返回對象,如果爲空則返回or()方法中預設的值。
  • ifPresentOrElse() 方法有兩個參數:一個 Consumer 和一個 Runnable。如果對象不爲空,會執行 Consumer 的動作,否則運行 Runnable。相比ifPresent()多了OrElse判斷。
  • stream()將Optional轉換成stream,如果有值就返回包含值的stream,如果沒值,就返回空的stream。

  因爲這個jdk1.9的Optional具體我沒有測試,同時也發現有蠻好的文章已經也能讓大家明白jdk1.9的option的優化,我就不深入去說了。

 

 

 

 

原文參考公衆號【Java知音】

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章