null 指標到處都有!
在一個 Java 程式員所能遇到的所有異常中,null 指標異常屬於最恐怖的,這是因為:它是程式能給出的資訊最少的異常。例如,不像一個類轉型異常,null 指標異常不給出它所需要的內容的任何資訊,只有一個null 指標。此外,它並不指出在代碼的何處這個null 指標被賦值。在許多null 指標異常中,真正的錯誤出現在變數被賦為空白值的地方。為了發現錯誤,我們必須通過控制流程跟蹤,以發現變數在哪裡被賦值,並確定是否這麼做是不正確的。當賦值出現在包中,而不是出現在發生報錯的地方時,進程會被明顯地破壞。
許多 Java 開發人員告訴我,他們所遇到的絕大多數程式崩潰是null 指標異常,並且他們渴望有一種工具,能在程式第一次運行前靜態地識別出這些錯誤。不幸的是,自動控制理論告訴我們,沒有工具可以靜態地決定哪些程式將拋出null 指標異常。但是在一個程式中,用一個工具排除許多null 指標異常是有可能的,留給我們僅僅一小部分需要我們必須人工檢查的潛在的問題所在。實際上,為了為 Java 程式(請參閱 參考資料)提供這樣一個工具,現在正做著一些研究。但是一個好的工具也只能為我們做這些。null 指標異常將決不會被完全根除。當它們真的發生時,工具能幫我們弄清和它們相聯絡的錯誤類型,這樣我們能快速診斷它們。另外,我們可以應用某些編程和設計技巧來顯著減少這些類型錯誤的出現。
懸掛複合類型
我們將探討的第一個關於null 指標異常的錯誤類型,是一個我稱之為懸掛複合類型的錯誤類型。這種類型的錯誤是這樣產生的:定義的某些基本例沒有被給出它們自己的類,然後以這種方法定義了一個遞迴的資料類型。相反,null 指標被插入到不同的複合資料型別中。資料類型執行個體的使用就好像null 指標被正確填充了一樣。我稱之為懸掛複合類型是因為衝突代碼是複合設計類型的一個有缺點的應用程式,其中,複合資料型別包含懸掛的引用(也就是null 指標)。
原因
考慮下面 LinkedList 類的單串連執行,它有一個懸掛複合類型。為了樣本的簡單起見,我只執行在 java.util.LinkedList 中定義的一些方法。為了顯示這種類型的錯誤是多麼隱蔽,我已經在下面代碼中引入一個錯誤。看看你是否能發現它。
>清單 1. 單串連鏈表
import java.util.NoSuchElementException;
public class LinkedList {
private Object first;
private LinkedList rest;
/**
* Constructs an empty LinkedList.
*/
public LinkedList() {
this.first = null;
this.rest = null;
}
/**
* Constructs a LinkedList containing only the given element.
*/
public LinkedList(Object _first) {
this.first = _first;
this.rest = null;
}
/**
* Constructs a LinkedList consisting of the given Object followed by
* all the elements in the given LinkedList.
*/
public LinkedList(Object _first, LinkedList _rest) {
this.first = _first;
this.rest = _rest;
}
}