Java程式設計中空指標(NullPointerException)的防範經驗分享

擁抱心中的夢想發表於2018-11-07

在寫程式碼的過程中,出現最多的異常可能就是空指標異常了。說白了,空指標異常就是你拿一個不存在的物件,去訪問它的成員屬性或者方法。我們暫且看下面的程式碼:

public static String getString() {
    return null;
}

public static void main(String[] args) {
    // 此處返回null
    String str = getString();
    System.err.println(str.length());
}
複製程式碼

很明顯,上面程式執行的結果肯定是排除煩人的java.lang.NullPointerException:

Exception in thread "main" java.lang.NullPointerException
	at org.springframework.web.SpringServletContainerInitializer.main(SpringServletContainerInitializer.java:187)
複製程式碼

但是,其實空指標本身並不是那麼可怕,可怕的是命名我們知道有這麼一種可能會出現空指標異常,但是我們卻不知道或者沒法提前預判,就像上面的程式碼片段,在eclipse IDE中是沒有任何提示的,如下圖:

Java程式設計中空指標(NullPointerException)的防範經驗分享

那麼有什麼辦法我們可以提前預判到可能會丟擲NullPointerException的程式碼塊,然後做防空處理呢!答案是肯定的。下面我將介紹幾種常見的防空方式,注意,這不是演習,這不是演習,這不是演習。哈哈!

  • 1、使用註解方式標識引數、方法返回值是否為空
  • 2、使用Java8提供的Optional類宣告我們的API

下面我將一一舉例進行探討(IDE以eclipse為例):

前置處理:

首先我們要在eclipse啟用基於註解的檢查,在Windows->Preference->Java->Compiler->Errors/Warnings->Null Analysis,如下圖所示:

Java程式設計中空指標(NullPointerException)的防範經驗分享

一、使用註解方式標識引數、方法返回值是否為空

JSR 303/305和Spring都提供了相應的的註解,如下所示:

  • 1、JSR 303/305:@Nonnull@Nullable
  • 2、Spring:@NonNull@Nullable

在eclipse啟動空檢驗之後,要想使上面的@Nullable註解生效,還需要將註解的全路徑型別配置到上圖的Use default annotations for null sercifications,配置如下圖:

Java程式設計中空指標(NullPointerException)的防範經驗分享

配置完成之後,再看下面程式碼:

/**
 * 表示返回的值可能為空
 * @return
 */
@Nullable
public static String getString() {
    return null;
}

/**
 * 引數不能為空
 * @param str
 * @return
 */
public static String getName(@NonNull String str) {
    return null;
}

public static void main(String[] args) {
    // 此處返回null
    String str = getString();
    System.err.println(str.length());
    
    // 引數為空
    getName(null);
}
複製程式碼

Java程式設計中空指標(NullPointerException)的防範經驗分享

這樣子可能為空的程式碼就一目瞭然了。當然了,其中的原理還是IDE支援掃描JSR規範的相關注解,Spring的@NonNull@Nullable也是基於JSR註解實現的,可用於宣告方法,欄位或者引數。

二、Optional

Java8提供了對null進行建模的類,它叫Optional。我們可以把它理解為一個容器,容器裡儲存的,可以是方法的放回值,也可以是方法引數值,如果方法返回值為null或者引數值為null,那麼就返回一個空容器物件。請看下面程式碼:

/**
 * 表示返回的值可能為空
 * @return
 */
public static Optional<String> getString() {
    // 返回空容器,裡邊什麼也沒有
    return Optional.empty();
}

/**
 * 引數不能為空
 * @param str
 * @return
 */
public static String getName(Optional<String> str) {
    return str.get();
}

public static void main(String[] args) {
    // 此處返回Optional.empty()
    Optional<String> optional = getString();
    // 如果返回的是空容器,那麼就返回orElse的值,否則,返回容器裡的值
    String value = optional.orElse("otherValue");
    // 輸出otherValue
    System.err.println(value);
    
    // 引數為空
    getName(Optional.of("value"));
}
複製程式碼

使用Optional類很方便對可能返回null的值進行建模,同時Optional裡邊提供了很多有用的方法允許我們傳入一個lambda表示式進行處理,非常方便。

總結

  • 1、如果只是單純想處理程式碼邏輯中可能出現的null異常,那麼其實我覺得使用IDE結合註解的方式可能會更加方便一點,當然使用Optional也是可以的
  • 2、如果我們想提供一個對外的API,我認為將API的成員屬性、方法返回值宣告為Optional可能會更好一點,因為別人一看就知道,該API方法簽名可能會返回空值,使用者只需要用一個Optional對接收即可!
  • 3、要想減少空異常,關鍵還得提升自己的編碼能力!身正不怕影子斜。

相關文章