1. 程式人生 > >Java中如何處理空指標異常

Java中如何處理空指標異常

在Java語言在,null被分配給一個物件的引用以表示物件指向未知資料塊。當應用程式使用或訪問一個指向null的引用,會被丟擲。 下列情況會丟擲NullPointerException 。

  •  呼叫null物件的方法。
  • 訪問或修改null物件的域。
  • 如果null是一個數組,並獲取null的長度。
  • 如果null物件是一個物件陣列,並訪問會修改null物件的子元素。
  • 如果物件是一個 Throwable值,並丟擲null。
  • 試圖對null物件同步。
   NullPointerException 是一個 RuntimeException ,Javac編譯器不會強制你使用try-catch塊來處理該異常。

         為什麼我們需要null值?


 
         如前所述,null是一個Java中的特殊值。null在編寫一些設計模式時非常有用。例如Null物件模式[1] 與單例模式。Null物件模式提供一個物件作為缺少給定型別物件的代理。單例模式確保只有一個類的例項被建立,並且提供一個物件的全域性訪問點。
        例如,至多建立類的一個例項的一種簡單的方法是宣告類的所有建構函式給私有型別,並建立一個公共方法,該方法返回這個類的唯一例項。

      TestSingleton.java:    

import java.util.UUID;

class Singleton {

     private static Singleton single = null;
     private String ID = null;

     private Singleton() {
          /* Make it private, in order to prevent the creation of new instances of
           * the Singleton class. */

          ID = UUID.randomUUID().toString(); // Create a random ID.
     }

     public static Singleton getInstance() {
          if (single == null)
               single = new Singleton();

          return single;
     }

     public String getID() {
          return this.ID;
     }
}

public class TestSingleton {
     public static void main(String[] args) {
          Singleton s = Singleton.getInstance();
          System.out.println(s.getID());
     }
在這個例子中,我們聲明瞭一個Singletion類的靜態例項。這個例項被在getInstance 方法中被初始化一次。注意,使用null值能使我們能夠建立唯一的例項。

        如何避免空NullPointerException

為了避免NullPointerException,確保所有物件在你使用之前被初始化。注意,當你宣告一個變數的引用時,你正在建立一個指向一個物件的指標。在你使用該物件的方法或域之前,你必須檢驗指標不為null。

   如果一個異常被丟擲,使用在異常棧軌跡中駐留的資訊。執行程式的棧軌跡由JVM提供,為了能夠除錯應用程式。定位產生異常的方法及異常被捕獲所在的行,以找出在哪些行上,哪些引用為null。在本節剩餘部分,我們將描述一些處理前文提到的異常所使用的技術。

        1. 帶有字面值的字串比較

        在應用程式程式碼中一種常見的情況是將字串變數與字面值進行比較。字串字面值可能是字串或列舉元素。我們將通過使用字面值來呼叫方法,而不是通過使用null物件來呼叫方法。例如,觀察下面的例子:

String str = null;
if(str.equals("Test")) {
     /* The code here will not be reached, as an exception will be thrown. */
}

        上面的程式碼片段會丟擲NullPointerException。然後,如果通過字面值來呼叫方法,程式會正常執行。

String str = null;
if("Test".equals(str)) {
     /* Correct use case. No exception will be thrown. */
}
        2.檢查一個方法的引數
        在執行方法之前,確保檢查了引數是否為null.當引數被適當檢查後,方法會繼續執行。否則,你可以丟擲IllegalArgumentException並且通知呼叫方法傳入的引數有誤。
public static int getLength(String s) {
     if (s == null)
          throw new IllegalArgumentException("The argument cannot be null");

     return s.length();
}

        3 優先使用String.valueOf()而不是toString() 
       當你的應用程式程式碼如要一個物件的字串來描述時,避免使用物件的toString方法。如果你的物件的引用為null,NullPointerException將會被丟擲。反之,考慮使用靜態方法String.valueOf(),該方法不會丟擲任何異常並且在函式引數為null的情況下會列印null。

        4. 使用三元運算子
        三元運算子能幫助我們避免NullPointerException.運算子具有這樣的形式:

        boolean expression ? value1 : value2;

       三元運算子能幫助我們避免NullPointerException.運算子具有這樣的形式:首先,計算布林表示式,如果表示式為true,value1被返回,否則value2被返回。我們能使用三元運算子來處理null指標,例如:

       String message = (str == null) ? "" : str.substring(0, 10);

       變數message將為空,如果str的引用為null,否則,如果str指向實際的資料,message將獲取str的前10個字元。

       5.建立返回空集合而不是null的方法
       一種非常好的技術是建立一個返回空集合的方法,而不是返回null值。你的應用程式程式碼可以迭代空集合並使用它的方法和域,而不會丟擲NullPointerException。例如:

public class Example {
     private static List<Integer> numbers = null;

     public static List<Integer> getList() {
          if (numbers == null)
               return Collections.emptyList();
          else
               return numbers;
     }
}
         6.利用Apache的 StringUtils類
         Apache的Commons Lang是一個庫,為java.lang API提供了幫助工具,例如字串操作方法。StringUtils.java提供了字串的操作,該類處理字串物件為null的情況。你可以使用StringUtils.isNotEmpty, StringUtils.IsEmpty 及 StringUtils.equals 方法,以避免NullPointerException。

         7. 使用contains(), containsKey(), containsValue() 方法
         如果你的程式使用了像Maps這樣的集合,考慮使用contains(), containsKey(), containsValue()方法。例如,在驗證某些鍵存在與Map中時,返回特定鍵的值。

Map<String, String> map = …
…
String key = …
String value = map.get(key);
System.out.println(value.toString()); // An exception will be thrown, if the value is null.
       在上面的片段中,我們不檢查鍵是否存在與Map中,返回值可能為null.最安全的方式是:
Map<String, String> map = …
…
String key = …
if(map.containsKey(key)) {
     String value = map.get(key);
     System.out.println(value.toString()); // No exception will be thrown.
}
       8.檢查外部方法的返回值
      實際環境中,使用外部的庫很常見。這些庫包含返回某個引用的方法。確保返回的引用不為null.閱讀javadoc的方法,以更好理解函式功能與返回值。
      9.使用斷言
      當測試程式碼時,斷言很有用。以避免執行會丟擲NullPointerException的方法。Java斷言的實現通過assert關鍵字並丟擲AssertionError.注意,你必須顯式通過-ea引數啟用斷言。否則斷言將會被忽略。
public static int getLength(String s) {
     /* Ensure that the String is not null. */
     assert (s != null);

     return s.length();
}

      如果你執行上述程式碼並傳遞null引數給getLength,將會出現下面的錯誤。

      最後,你可以使用由Junit測試框架提供的Assert類。

      存在NullPointerException的安全方法

      1.訪問靜態成員或類方法

        當你的程式碼試圖訪問靜態變數或一個類的方法,即使物件的引用等於null,JVM也不會丟擲NullPointerException.這是因為,在編過程中,Java編譯器儲存靜態方法和域在特殊的位置。靜態方法和域不與物件關聯,而是與類名關聯。

        例如下面的程式碼不會丟擲NullPointerException.

class SampleClass {

     public static void printMessage() {
          System.out.println("Hello from Java Code Geeks!");
     }
}

public class TestStatic {
     public static void main(String[] args) {
          SampleClass sc = null;
          sc.printMessage();
     }
}
        注意,儘管SampleClass的例項為null,方法還是會被執行。當方法或域為靜態時,應該以“靜態”的方式來訪問,即通過類名來訪問。例如:SampleClass.printMessage() 

        2. instanceof 操作符
        即使物件的引用為null,instanceof操作符可使用。當引用為null時,instanceof 操作符返回false,而且不會丟擲NullPointerException.例如,下面的程式碼:

String str = null;
if(str instanceof String)
     System.out.println("It's an instance of the String class!");
else
     System.out.println("Not an instance of the String class!");

結果如下:

Not an instance of the String class!

[1] Null Object pattern, https://en.wikipedia.org/wiki/Null_Object_pattern.

英文原文地址:http://examples.javacodegeeks.com/java-basics/exceptions/java-lang-nullpointerexception-how-to-handle-null-pointer-exception/?utm_source=tuicool