国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

避免使用空指針作為標志以防止出現異常

2019-11-18 15:12:29
字體:
來源:轉載
供稿:網友

  避免使用空指針作為標志以防止出現異常

Eric E. Allen
軟件工程師,Cycorp, Inc.

為開發健壯的程序,我們經常用空指針代替異常情況,但這實際上卻把控制流限制在方法調用和返回的普通方式,同時也隱藏了異常情況發生的跡象。在這篇專欄里,Eric Allen 展示了這種錯誤模式(他稱之為空標志錯誤模式)怎樣產生難以調試的意外結果。和我們討論過的其它錯誤模式一樣,您可以應用某種編程技巧來減少這種錯誤的出現。
空標志錯誤模式
在我的上一篇文章中,我說明了用空指針代替各種不同基本類型的數據是如何成為引起 NullPointerException 異常最普遍的原因之一的。這一次,我將說明用空指針代替異常情況怎么也會導致問題的出現。在 java 程序中,異常情況通常是通過拋出異常,并在適當的控制點捕捉它們來進行處理。但是經常看到的方法是通過返回一個空指針值來表明這種情況(以及,可能打印一條消息到 System.err)。假如調用方法沒有明確地檢查空指針,它可能會嘗試丟棄返回值并觸發一個空指針異常。

您可能會猜想,之所以稱這種模式為空標志錯誤模式,是因為它是不一致地使用空指針作為異常情況的標志引起的。

起因
讓我們來考慮一下下面的這個簡單的橋類(從 BufferedReaders 到 Iterators):

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
import java.util.Iterator;

public class BufferedReaderIterator implements Iterator {

PRivate BufferedReader internal;

public BufferedReaderIterator(BufferedReader _internal) {
this.internal = _internal;
}

public boolean hasNext() {
try {

boolean result = true;

// Let′s suppose that lines in the underlying input stream are known
// to be no greater than 80 characters long.
internal.mark(80);

if (this.next() == null) {
result = false;
}
internal.reset();
return result;
}
catch (IOException e) {
System.err.println(e.toString());
return false;
}
}

public Object next() {
try {
return internal.readLine();
}
catch (IOException e) {
System.err.println(e.toString());
return null;
}
}

public void remove() {

// This iterator does not support the remove Operation.
throw new UnsupportedOperationException();
}

}

因為這個類作為 Iterator 接口的橋接實現,代碼必須從 BufferedReader 捕捉 IOException 異常。每一種方法通過返回某個缺省值來處理 IOException。對于 hasNext,返回 false 值。這是合理的,因為假如 IOException 異常被拋出,客戶就不應該指望能從 Iterator 檢索到另一個元素。另一方面,在 IOException 異常(因為它取決于 internal.readLine() 的返回值)和 internal 是空的情況下,next 都返回 null。但這不是 Iterator 對象的客戶所期待的。正常情況下,在沒有更多元素的 Iterator 上調用 next 時,會拋出一個 NoSUChElementException 異常。假如我們的 Iterator 的客戶依靠于這種行為,它很可能會嘗試丟棄從調用 next 返回的空指針,結果導致 NullPointerException 異常。

不管 NullPointerException 異常什么時候出現,都要對如上所述的情況作檢查。這種錯誤模式的出現很普遍。

預防措施
盡管這種錯誤模式經常出現,使用空標志仍是非常沒有根據的(與上例的情況一樣)。讓我們來重寫 next,使它如我們期望的一樣拋出 NoSuchElementException 異常:

public Object next() {
try {
String result = internal.readLine();
if (result == null) {
throw new NoSuchElementException();
}
else {
return result;
}
}
catch (IOException e) {

// The original exception is included in the message to notify the
// client that an IOException has occurred.
throw new NoSuchElementException(e.toString());
}
}

請注重:要使其余的代碼能使用修改過的方法,我們還必須:

導入 java.util.NoSuchElementException。
修正 hasNext,使其不再調用 next 來進行測試。最簡單的修正方法是只要直接調用 internal.readLine()。

另一種處理 IOException 異常的方法是捕捉它們,并代替它們拋出 RuntimeException 異常。決定這樣做是基于對目標平臺上預計 IOException 異常出現頻率的估計。假如很頻繁,那么您可能想試著從中恢復。

調用這個新 next 方法的任何代碼可能都不得不處理拋出的 NoSuchElementException 異常。(當然,代碼可以簡單地選擇忽略它們并答應程序異常終止。)假如這樣,與原始代碼拋出的 NullPointerException 異常相比,產生的錯誤消息和拋出異常的位置所提供的信息要豐富得多。假如拋出的異常是檢查過的異常(比如 IOException),那么它會更有用,因為除非處理了異常,否則類的客戶代碼將不編譯。利用這種方法,我們甚至可以在程序運行前排除某些錯誤發生的可能性。但是,在這個示例中,不破壞 Iterator 接口,就不能拋出這樣一個檢查過的異常。因此,為了重復使用在 Iterators 上運行的代碼,我們犧牲了一些靜態檢查。靜態檢查的目的和重復使用的目的之間的這種矛盾是很普遍的。

總結
在我完成這篇文章前,我要提醒許多經常使用空標志的程序員注重。許多程序員會爭辯說這會使他們的程序更“健壯”。究竟,他們可能會說,健壯的系統能夠適當地處理不同的情況,而不是一碰到小問題就拋出異常。但是這種爭辯忽視了這樣一種事實,即異常是增強代碼健壯性的有力工具,它答應在異常情況下控制能快速傳送到最適合控制的位置。另一方面,空標志的使用把控制流限制在方法調用和返回的普通方式(當然,一直到整個程序崩潰)。此外,這樣使用空標志,程序員有效地掩蓋了異常情況出現位置的跡象。誰知道空指針在被丟棄前從方法到方法傳遞了多遠?這只能使得診斷錯誤以及確定怎樣修正它們更加困難。經驗證實這種代碼經常中斷。我們首要關注的應該是避免這種困惑,使診斷盡可能輕易。因此,作為準則,我努力編寫可以盡快通知異常情況的代碼,并且嘗試著僅從沒有指示程序錯誤的異常情況中恢復。

即使在代碼中盡量避免使用空標志,您仍要不可避免地處理使用了空標志的舊代碼。事實上,許多 Java 類庫本身,比如我們上面使用過的 Hashtable 類和 BufferedReader 類都用了空標志。當使用這樣的類時,您可以通過在執行前,顯式檢查操作是否將返回空來避免錯誤。例如,對于 Hashtables,我總是在調用 get 之前用 containsKey 進行測試。但是,盡管采用這種預防手段,這種錯誤模式仍然是最常碰到的錯誤模式之一。

下面是本周的錯誤模式的小結:

模式:空標志
癥狀:使用空指針作為異常情況的標志的代碼塊報告 NullPointerException 異常。
起因:調用方法沒有檢查作為返回值的空指針。
治療和預防措施:拋出異常來報告異常情況。

在下一篇文章中,我將討論與類強制轉換異常有關的錯誤模式。

參考資料

請務必閱讀 Eric Allen 的前一個關于錯誤模式的診斷 Java 代碼專欄:
The Dangling Composite bug pattern(developerWorks,2001 年 3 月)
錯誤模式:介紹(developerWorks ,2001 年 2 月)
一種防止異常情況處理不一致的方法是面向表征的編程: 一種將程序的經常繞過模塊邊界的部分模塊化的編程風格。請查看 aspectJ,Java 語言的一種面向表征的擴展,帶有支持許多流行的 Java IDE 的實現。
靜態確定可能出現空指針異常的方法是一種被稱為 set-based analysis 的技術。The Carnegie Mellon School of Computer Science 網站為這種方法提供了簡短介紹,同時還提供與本文相關的一些技術出版物的鏈接。
DePaul 大學的軟件工程部已經在自動化定理方面做了一些工作,在 Java 代碼中偵測出空指針異常。
訪問 Patterns 主頁,獲取關于設計模式以及怎樣使用它們的好的介紹。
請查看 JUnit,通過將代碼 "test-infested" 來捕捉更多的錯誤。

關于作者
Eric Allen 畢業于 Cornell 大學,曾獲得計算機科學和數學的學士學位。目前是 Cycorp, Inc. 的 Java 軟件開發的帶頭人以及 Rice 大學編程語言小組的半工半讀研究生。他的研究涉及正規語義模型和 Java 語言的擴展,都是在源代碼和字節代碼的級別上的。目前,他正在為 NextGen 編程語言實現一種從源代碼到字節代碼的編譯器,這也是 Java 語言的泛型運行時類型的一種擴展。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 临江市| 阜阳市| 万荣县| 克东县| 鲁山县| 米易县| 鄂尔多斯市| 亚东县| 融水| 大埔县| 洞头县| 瑞昌市| 泌阳县| 重庆市| 贡山| 秦皇岛市| 崇左市| 淅川县| 大关县| 拉萨市| 永定县| 西乌珠穆沁旗| 雷州市| 泌阳县| 威信县| 沽源县| 柳州市| 深州市| 济阳县| 北碚区| 沅江市| 合水县| 壶关县| 沧源| 乐都县| 蒲城县| 定兴县| 丰城市| 乌拉特后旗| 弥渡县| 偏关县|