Optional 類
上一小節,我們接觸到了Optional
類,但沒有詳細展開介紹,Optional
類也是 Java 8 新加入的類。本小節我們就來學習一下這個類,你將了解到Optional
類的解決了什么問題,如何創建Optioanl
類的對象,它又有哪些常用方法,如何在實際開發中應用Optional
類等內容。
1. Optional 類概述
空指針異常(NullPointerExceptions
)是 Java 最常見的異常之一,一直以來都困擾著 Java 程序員。一方面,程序員不得不在代碼中寫很多null
的檢查邏輯,讓代碼看起來非常臃腫;另一方面,由于其屬于運行時異常,是非常難以預判的。
為了預防空指針異常,Google
的Guava
項目率先引入了Optional
類,通過使用檢查空值的方式來防止代碼污染,受到Guava
項目的啟發,隨后在Java 8
中也引入了Optional
類。
Optional 類位于java.util
包下,是一個可以為 null
的容器對象,如果值存在則isPresent()
方法會返回 true
,調用 get()
方法會返回該對象,可以有效避免空指針異常。下面我們來學習如何實例化這個類,以及這個類下提供了哪些常用方法。
2. 創建 Optional 對象
查看 java.util.Optional
類源碼,可以發現其構造方法是私有的,因此不能通過new
關鍵字來實例化:

我們可以通過如下幾種方法,來創建Optional 對象:
Optional.of(T t)
:創建一個 Optional 對象,參數t
必須非空;Optional.empty()
:創建一個空的Optional
實例;Optional.ofNullable(T t)
:創建一個Optional
對象,參數t
可以為null
。
實例如下:
import java.util.Optional;
public class OptionalDemo1 {
public static void main(String[] args) {
// 創建一個 StringBuilder 對象
StringBuilder string = new StringBuilder("我是一個字符串");
// 使用 Optional.of(T t) 方法,創建 Optional 對象,注意 T 不能為空:
Optional<StringBuilder> stringBuilderOptional = Optional.of(string);
System.out.println(stringBuilderOptional);
// 使用 Optional.empty() 方法,創建一個空的 Optional 對象:
Optional<Object> empty = Optional.empty();
System.out.println(empty);
// 使用 Optional.ofNullable(T t) 方法,創建 Optional 對象,注意 t 允許為空:
stringBuilderOptional = null;
Optional<Optional<StringBuilder>> stringBuilderOptional1 = Optional.ofNullable(stringBuilderOptional);
System.out.println(stringBuilderOptional1);
}
}
運行結果:
Optional[我是一個字符串]
Optional.empty
Optional.empty
3. 常用方法
Optional<T>
類提供了如下常用方法:
booean isPresent()
:判斷是否包換對象;void ifPresent(Consumer<? super T> consumer)
:如果有值,就執行 Consumer 接口的實現代碼,并且該值會作為參數傳遞給它;T get()
:如果調用對象包含值,返回該值,否則拋出異常;T orElse(T other)
:如果有值則將其返回,否則返回指定的other
對象;T orElseGet(Supplier<? extends T other>)
:如果有值則將其返回,否則返回由Supplier
接口實現提供的對象;T orElseThrow(Supplier<? extends X> exceptionSupplier)
:如果有值則將其返回,否則拋出由Supplier
接口實現提供的異常。
知道了如何創建Optional
對象和常用方法,我們下面結合具體實例來看一下,Optional
類是如何避免空指針異常的。
請查看如下實例,其在運行時會發生空指針異常:
import java.util.Optional;
public class OptionalDemo2 {
static class Category {
private String name;
public Category(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Category{" +
"name='" + name + '\'' +
'}';
}
}
static class Goods {
private String name;
private Category category;
public Goods() {
}
public Goods(String name, Category category) {
this.name = name;
this.category = category;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Category getCategory() {
return category;
}
public void setCategory(Category category) {
this.category = category;
}
@Override
public String toString() {
return "Good{" +
"name='" + name + '\'' +
", category=" + category +
'}';
}
}
/**
* 獲取商品的分類名稱
* @param goods 商品
* @return 分類名稱
*/
static String getGoodsCategoryName(Goods goods) {
return goods.getCategory().getName();
}
public static void main(String[] args) {
// 實例化一個商品類
Goods goods = new Goods();
// 獲取商品的分類名稱
String categoryName = getGoodsCategoryName(goods);
System.out.println(categoryName);
}
}
運行結果:
Exception in thread "main" java.lang.NullPointerException
at OptionalDemo2.getGoodsCategoryName(OptionalDemo2.java:73)
at OptionalDemo2.main(OptionalDemo2.java:80)
實例中,由于在實例化Goods
類時,我們沒有給其下面的Category
類型的屬性category
賦值,它就為 null
,在運行時, null.getName()
就會拋出空指針異常。同理,如果goods
實例為null
,那么null.getCategory()
也會拋出空指針異常。
在沒有使用Optional
類的情況下,想要優化代碼,就不得不改寫getGoodsCategoryName()
方法:
static String getGoodsCategoryName(Goods goods) {
if (goods != null) {
Category category = goods.getCategory();
if (category != null) {
return category.getName();
}
}
return "該商品無分類";
}
這也就是我們上面說的null
檢查邏輯代碼,此處有兩層if
嵌套,如果有更深層次的級聯屬性,就要嵌套更多的層級。
下面我們將Optional
類引入實例代碼:
import java.util.Optional;
public class OptionalDemo3 {
static class Category {
private String name;
public Category(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Category{" +
"name='" + name + '\'' +
'}';
}
}
static class Goods {
private String name;
private Category category;
public Goods() {
}
public Goods(String name, Category category) {
this.name = name;
this.category = category;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Category getCategory() {
return category;
}
public void setCategory(Category category) {
this.category = category;
}
@Override
public String toString() {
return "Good{" +
"name='" + name + '\'' +
", category=" + category +
'}';
}
}
/**
* 獲取商品的分類名稱(使用 Optional 類包裝)
* @param goods 商品
* @return 分類名稱
*/
static String getGoodsCategoryName(Goods goods) {
// 將商品實例包裝入 Optional 類,創建 Optional<Goods> 對象
Optional<Goods> goodsOptional = Optional.ofNullable(goods);
Goods goods1 = goodsOptional.orElse(new Goods("默認商品", new Category("默認分類")));
// 此時 goods1 一定是非空,不會產生空指針異常
Category category = goods1.getCategory();
// 將分類實例包裝入 Optional 類,創建 Optional<Category> 對象
Optional<Category> categoryOptional = Optional.ofNullable(category);
Category category1 = categoryOptional.orElse(new Category("默認分類"));
// 此時 category1 一定是非空,不會產生空指針異常
return category1.getName();
}
public static void main(String[] args) {
// 實例化一個商品類
Goods goods = null;
// 獲取商品的分類名稱
String categoryName = getGoodsCategoryName(goods);
System.out.println(categoryName);
}
}
運行結果:
默認分類
實例中,我們使用Optional
類的 ofNullable(T t)
方法分別包裝了goods
對象及其級聯屬性category
對象,允許對象為空,然后又調用了其ofElse(T t)
方法保證了對象一定非空。這樣,空指針異常就被我們優雅地規避掉了。
4. 對于空指針異常的改進
Java 14 對于空指針異常有了一些改進,它提供了更明確異常堆棧打印信息,JVM 將精確地確定那個變量是null
,不過空指針異常依然無法避免。明確的異常堆棧信息,能夠幫助開發者快速定位錯誤發生的位置。
5. 小結
通過本小節的學習,我們知道了 Optional
類主要用于應對 Java 中的空指針異常,它是一個可以為 null
的容器對象,我們可以通過Optional
類下的幾個靜態方法來創建對象。另外,我們也結合實例介紹了如何使用Optional
類來規避空指針異常,實例中還有很多其他沒用到的 API,希望大家可以自己研習。