簡體   English   中英

是否有一個空指針異常代碼氣味?

[英]Is Catching a Null Pointer Exception a Code Smell?

最近我的一個同事用一些代碼寫了一個代碼來捕獲整個方法的空指針異常,然后返回一個結果。 我指出了空指針有多種原因,所以我們將其改為對一個結果進行防御性檢查。

但是,捕獲NullPointerException似乎對我來說不對。 在我看來,Null指針異常是錯誤代碼的結果,而不是系統中的預期異常。

是否有任何情況下捕獲空指針異常是有意義的?

是的,捕獲任何RuntimeException幾乎總是代碼味道。 C2 Wiki似乎同意。

一個例外可能是一些特別防御的代碼片段,它們運行來自其他模塊的大量隨機代碼。 這種防御結構的例子是EDT ,ThreadPools / Executors和插件系統。

我可以想到一個用於捕獲NullPointerException用法:

catch (NullPointerException) {
    ApplyPainfulElectricShockToProgrammer();
}

由於第三方庫中的錯誤,我有時必須捕獲nullpointer異常。 我們使用的庫拋出了這個異常,我們無能為力。

在這種情況下,抓住它是可以的 ,否則不會。

這取決於。

這位同事的經驗如何? 他這樣做是為了無知/懶惰還是有一個真正的理由呢? (就像這是主要線程高於一切,永遠不應該死?)

捕獲運行時異常的90%是錯誤的,99%捕獲NullPointerException是錯誤的(如果原因是“我得到了很多......”那么整個程序員都錯了,你應該看看照顧他正在做的其余代碼)

但在某些情況下,捕獲NullPointerException可能是可以接受的。

這很糟糕,但它可以產生優化的字節碼。

如果Integer i在大多數時間不為null ,那么檢查會降低整體性能。 支票本身需要3條指令(0-4)。 然后整個案例需要7條指令(0-14)。

public class IfNotNull {

    public Integer i;

    public String getIAsString() {
        if (i != null) {
            return i.toString();
        } else {
            return "";
        }
    }
}

  public java.lang.String getIAsString();
    Code:
       0: aload_0       
       1: getfield      #2                  // Field i:Ljava/lang/Integer;
       4: ifnull        15
       7: aload_0       
       8: getfield      #2                  // Field i:Ljava/lang/Integer;
      11: invokevirtual #3                  // Method java/lang/Integer.toString:()Ljava/lang/String;
      14: areturn       // <- here we go
      15: ldc           #4                  // String 
      17: areturn 

遵循EAFP方法,這在Python世界中很常見。 null情況代價很高,但對於not null情況我們只需要4條指令(0-7)。

public class TryCatch {

    public Integer i;

    public String getIAsString() {
        try {
            return i.toString();
        } catch (NullPointerException npe) {
            return "";
        }
    }
}

  public java.lang.String getIAsString();
    Code:
       0: aload_0       
       1: getfield      #2                  // Field i:Ljava/lang/Integer;
       4: invokevirtual #3                  // Method java/lang/Integer.toString:()Ljava/lang/String;
       7: areturn       // <- here we go
       8: astore_1      
       9: ldc           #5                  // String a
      11: areturn       
    Exception table:
       from    to  target type
           0     7     8   Class java/lang/NullPointerException

誰知道,如果JIT編譯器可以優化這個?

一般來說,我認為這是一種代碼味道; 在我看來,防御性檢查更好。 我會擴展它以涵蓋大多數未經檢查的異常,除了事件循環等,它們想要捕獲報告/日志記錄的所有錯誤。

我能想到的例外是圍繞對一個無法修改的庫的調用,並且可能會生成一個空指針異常,以響應一些難以主動檢查的斷言失敗。

滑稽

我發現了一些不應該在工作中完成的事情:

public static boolean isValidDate(final String stringDateValue) {
    String exp = "^[0-9]{2}/[0-9]{2}/[0-9]{4}$";
    boolean isValid = false;
    try {
        if (Pattern.matches(exp, stringDateValue)) {
            String[] dateArray = stringDateValue.split("/");
            if (dateArray.length == 3) {
                GregorianCalendar gregorianCalendar = new GregorianCalendar();
                int annee = new Integer(dateArray[2]).intValue();
                int mois = new Integer(dateArray[1]).intValue();
                int jour = new Integer(dateArray[0]).intValue();

                gregorianCalendar = new GregorianCalendar(annee, mois - 1,
                        jour);
                gregorianCalendar.setLenient(false);
                gregorianCalendar.get(GregorianCalendar.YEAR);
                gregorianCalendar.get(GregorianCalendar.MONTH);
                gregorianCalendar.get(GregorianCalendar.DAY_OF_MONTH);
                isValid = true;
            }
        }
    } catch (Exception e) {
        isValid = false;
    }
    return isValid;
}

baaad :)

開發人員希望日歷能夠引發此類異常:

java.lang.IllegalArgumentException: DAY_OF_MONTH
    at java.util.GregorianCalendar.computeTime(GregorianCalendar.java:2316)
    at java.util.Calendar.updateTime(Calendar.java:2260)
    at java.util.Calendar.complete(Calendar.java:1305)
    at java.util.Calendar.get(Calendar.java:1088)

使值無效......

是的它有效,但這不是一個很好的做法......

提高異常(特別是填充堆棧跟蹤)的成本遠遠超過僅手動檢查數據而無異常...

必然是。

大多數情況下,您的變量開頭不應為null。 許多新語言都出現了對非可空引用類型的內置支持 - 也就是說,保證永遠不會為null的類型。

對於允許傳入值為空的時間,您需要進行檢查。 但是,例外絕對是一種不好的方法。

if語句可能需要執行三條指令,並且是本地檢查(意味着,您需要在需要保證的同一位置進行檢查)。

另一方面,使用異常可能需要更多指令 - 系統嘗試查找方法,失敗,查看異常表中的相應異常處理程序,跳轉到那里,執行處理程序,然后再次跳轉。 此外,檢查可能是非本地的。 如果您的代碼是這樣的:

try
  return contacts.find("Mom").getEmail()
catch (NullPointerException e)
  return null

您不知道NPE是在“getEmail”還是“find”中拋出。

對於以更混淆的方式編寫的非常非常常見的模式的技術上更糟糕的解決方案? 這不是排名,但它肯定聞起來很糟糕:/

你應該捕獲NullPointerException(或者特別是任何Throwable)的唯一地方是在某個頂層或系統邊界,這樣你的程序就不會完全崩潰並且可以恢復。 例如,在web.xml中設置錯誤頁面提供了一個全面的功能,以便Web應用程序可以從異常中恢復並通知用戶。

那這個呢:

try
{
    foo.x = bar;
}
catch (NullPointerException e)
{
    // do whatever needs to be done
}

作為一個微優化,當foo可能為null,但幾乎從來不是?

這個想法是這樣的:

  • 顯式NULL檢查需要單個機器指令

  • 另一方面,第二個版本中的NULL檢查可以通過允許NULL訪問,捕獲SIGSEGV和拋出NullPointerException來完成。 如果對象不為NULL,則這是免費的。

很久以前我有一次使用。 當按鍵檢索集合中的對象並且找不到對象時,一個特別愚蠢的庫會拋出NullPointerException。 沒有其他方法可以通過鍵查找,也無法檢查對象是否存在。

一段時間后,我們啟動了供應商並開始修改庫。 現在,庫會拋出一個更好的異常(我的更改)並具有檢查功能(其他人的更改)。

當然,我總是在try塊中只有一行。 還有我自己會犯錯誤的代碼。

捕獲NULL指針異常實際上取決於上下文...應該努力避免嚴格的絕對規則...應該在上下文中應用規則 - 想要捕獲此異常並將整個軟件置於某種STABLE狀態 - 什么也不做幾乎沒有。 應該很好地理解所有這些編碼規則

此時,您將查看您的軟件AUDIT TRACE ...您應該這樣做並發現此異常的來源。

永遠不會發生NULL指針異常的想法必須是可驗證的。 首先進行靜態分析...(如果第三方代碼/組件進入則更難),然后使用相關工具進行詳盡的狀態空間搜索。

X

實際上,捕獲NPE(實際上是任何RTE)可以干凈地終止基於Swing-GUI的應用程序。

編輯:在這種情況下,它通常通過UncaughtExceptionHandler完成。

我試圖保證我的接口的結果,但如果某些庫或某些代碼可以產生null,因此我希望保證捕獲它可能是可行的。 當然,一旦你抓到它,你所做的就取決於你。 有時檢查null是沒有意義的,如果你抓住了它,你還有其他一些解決問題的方法,可能不會那么好,但可以完成工作。

我所說的是使用例外,它是一個非常好的語言功能。

如果您的方法調用外部接口(或SOAP API)並且返回的值可能為Null,則捕獲NullPointerException非常有用。 除此之外,捕獲這些異常並沒有太大的好處。

這實際上取決於接口定義。 非結構化NPE處理與捕獲異常或Throwable一樣糟糕。

Null用於標識未初始化的狀態,而不是使用空字符串或max_int或其他任何內容。 一旦我定期使用null的位置是在回調對象不相關的地方。

我非常喜歡Guice提供的@Nullable注釋。

http://code.google.com/docreader/#p=google-guice&s=google-guice&t=UseNullable

要消除代碼庫中的NullPointerExceptions,必須遵守空引用的規定。 通過遵循並執行一條簡單的規則,我們在此方面取得了成功:

除非明確指定,否則每個參數都是非空的。 Google Collections庫和JSR-305具有簡單的API,可以控制空值。 如果找到空引用,則Preconditions.checkNotNull可用於快速失敗,並且@Nullable可用於注釋允許空值的參數。

Guice默認禁止null。 它將拒絕注入null,而不是使用ProvisionException。 如果您的類允許null,則可以使用@Nullable注釋字段或參數。 Guice識別任何@Nullable注釋,如edu.umd.cs.findbugs.annotations.Nullable或javax.annotation.Nullable。

在Java中是的,需要檢查NullPointerException。

當應用程序在需要對象的情況下嘗試使用null時拋出。 這些包括:

調用null對象的實例方法。 訪問或修改空對象的字段。 將null的長度視為數組。 訪問或修改null的槽,就像它是一個數組一樣。 拋出null就好像它是一個Throwable值。

應用程序應拋出此類的實例以指示null對象的其他非法使用。

讀取文本文件(即XML)時,其他語言中的NullPointerException,其記錄未經驗證為正確的ASCII字符和記錄格式。

如果程序員是初學者,他可能有習慣捕捉每一個阻止他獲得最終輸出的異常。 代碼審查員不應該接受這一點。

捕獲任何RuntimeException都很糟糕。 但是如果真的有必要,那么代碼中的注釋對將來使用該代碼的程序員來說真的很有幫助。 如果你不能寫出合理的評論來捕捉它們,那么你必須避免它們。 期。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM