什么时候可以捕获NullPointerException?

时间:2013-08-16 04:43:21

标签: java nullpointerexception

有效的java建议我们不应该catch NullPointerException。 总是对的吗?

在捕获NullPointerException的许多情况下,catch body仅调用printStackTrace()

如果我没有抓住NullPointerException并致电printStackTrace(),我该如何查看exception发生的地方?

而且,如果我抓住NullPointerException并且catch正文为空,那么我们当时无法获得任何堆栈信息,是吗?

更新

我分析了google android source,AOSP4.2.2_r1.2中捕获RuntimeException的统计信息。

有249个RuntimeException捕获,以下是catch-body的统计信息。

42%: throw it again as other Exceptions (RuntimeException: 33%, others: 8%)

32%: just return null or 0/false/true or other default values

14%: just call log or printstacktrace

5%: just comment like "// Fall through.", "// ignore, not a valid type.", "// system process dead", "// do nothing"

2%: empty body

3%: display error messages or send fail codes (ex. network service discovery failed: replyToMessage(msg, NsdManager.STOP_DISCOVERY_FAILED, NsdManager.FAILURE_INTERNAL_ERROR); )


3%: intialize or reset related variables.

在大多数情况下,dalvik和外部通过抛出其他例外来处理NPE。

但框架通常通过返回null或其他值来处理。

  1. 您是否认为将其他例外投入捕获体中并不错或处理好?

  2. 如果NPE出现在更高级别(例如应用程序)并且开发人员确认异常并不重要,因为应用程序是完全独立的,我可以接受我们的开发人员通过捕获来忽略NPE吗?

  3. 还有一件事,

    我可以得出结论,谷歌android框架源代码可能在RuntimeException方面有些不稳定吗?

10 个答案:

答案 0 :(得分:35)

  

有效的java建议我们不要捕获NullPointerException。总是对的吗?

几乎在所有情况下都是正确的。

NullPointerException通常是一个错误的结果;即,您的应用程序在未预料到的情况下遇到null对象引用,然后尝试使用它。在这种情况下,由于您(程序员)没有预料到null,因此几乎不可能知道尝试恢复是否安全,和/或知道什么是& #34;整治"可能是必需的。因此,最好的办法是让NPE传播到基本级别,然后将其视为通用错误。 (在"网络服务"应用程序中,可能适合返回"服务错误"响应,并尝试继续。)

另一种情况是您(程序员)预期可能会传递null。在这种情况下,最好的策略是(几乎总是)在尝试使用之前显式测试null ,从而避免 NPE ......以及处理它的需要。这有两个原因:

  • 异常处理通常很昂贵。实际上,它比<{1}}的测试成本高出许多个数量级

  • 如果您允许预期的NPE发生然后捕获它,您还有可能捕获其他意外的 NPE ......并且错误地处理它们。


请注意,我通过说&#34;几乎总是&#34;来证明上述内容。从理论上讲,有一种情况可能会导致null的显式测试使代码混乱,以至于至少值得考虑允许NPE发生。但是,仍然存在意外的NPE的可能性......取决于代码。所以这种方法总是可能脆弱。

(FWIW - 我从来没有遇到过这个好主意的真实案例......)


  

在很多捕获NullPointerException的情况下,catch body只调用printStackTrace()。

这可能是糟糕的代码。无所事事很少是从NPE中恢复的正确方法。

  

如果我没有捕获NullPointerException并调用printStackTrace(),我如何检查发生异常的地方?

您让NPE传播到基准级别。在那里你捕获并打印(或记录)所有未处理异常的堆栈跟踪,然后挽救或尝试恢复......如果可行的话。

  

而且如果我捕获NullPointerException并且catch主体为空,那么我们当时无法获得任何堆栈信息,是吗?

永远不要这样做!它被称为&#34;挤压&#34;并且危险。 (特别是因为,正如我上面所解释的那样,NPE可能是由于你/你的代码没有预料到的。)

不,如果你这样做,你就无法获得堆栈跟踪。它消失了。


<强>后续

对于避免NPE&#34; 1 的一些一般策略,我不太信任/信任。比如这样的东西:

null

总是让我怀疑程序员没有考虑问题。为什么return (someObject != null) ? someObject.toString() : ""; 首先是someObject

NPE是由null所在的地方引起的,而您并不期望它。因此,NPE通常是问题的症状而不是实际问题本身。在我看来,NPE是不可避免的。相反,您应该使用NPE来查找并修复意外null根本原因。像上面那样避免NPE的代码妨碍了这个目标。

所以我更喜欢/建议在意外的地方避免null值的策略。

  • 确保每个参考字段都被初始化为非空值...除非null有意义的值。

  • 尽量避免将null作为有意义的值,尤其是在有替代方法的情况下。例如,一个空字符串,一个零长度数组,一个空集合,一个表示&#34; undefined&#34;管他呢。或者,对于Java 8及更高版本,请使用null

  • 不要将Optional作为错误或特殊情况的指示返回。 (抛出异常或返回一个值。)

  • 提前检查未预料到的null值(例如null参数),然后尽快抛出NPE

  • null参数或结果合法的少数几个地方,请确保您的javadocs清楚明确地记录了这一点。如果没有文档,那么暗示应该是null不被允许并且不会被退回。

无论您何时获得NPE,请确保找到并修复问题的真实来源......而不仅仅是引发异常的具体声明。

1 - 了解标准Java API中使用(或滥用)null作为返回值的位置是有价值的。例如,nullClass.getResourceAsStream(...)。那些避免NPE的建议&#34;文件在指出这些陷阱方面非常有用。

答案 1 :(得分:2)

NullPointerException通常是由于代码中的逻辑错误而发生的。我们可以通过避免不安全的操作来消除NullPointerException。如果仍然发生任何NullPointerException,您可以在StackTrace中看到它。

例如

if (Obj.getName("Sam")) {
    // only executes if Obj != null
}  

我们可以避免NullPointerException如下

if (Obj == null){
    System.out.println("Null Obj, Can't proceed");
} else {
    Obj.getName("Sam")
}

答案 2 :(得分:2)

通常建议不应该抓住NullPointerException并让它由JVM处理。

但这一切都取决于, 比如说:如果您有List<Student> listStudent = getAllStudent();

之类的代码

这里getAllStudent()连接到DB并给你结果,这里在对列表对象进行任何操作之前你应该检查

if(listStudent != null){ 
    //do the task. 
}else{ 
    //Show Message like no student is there instead of catching NullPointerException 
}

如果你不这样做,那么如果没有数据可能会发生NullPointerException

所以更好的做法是检查Null但不像

那样处理它
try{ 

}catch(NullPointerException e){

}

答案 3 :(得分:1)

如果没有非常重要的原因,你肯定不应该使用空的catch块。通常没有这样的理由。

通常检查变量是否为空或捕获NPE以及更合适的原因是好的。例如,如果方法getCar()返回null,您可能会在尝试调用car的某个方法时捕获NPE,或检查该车是否为null并抛出{{1} (但只有当情况真的异常时)。因此,您可以使错误更具抽象性,更容易理解。

答案 4 :(得分:1)

当抛出异常时,无论是异常,jvm都会查找最接近的handler(catch块)。如果在当前方法中找不到它,它会在调用代码中查找处理程序,依此类推。基本上,从下到上搜索整个calling stack以查找可以处理所述异常的catch块。如果没有找到处理程序,jvm使用调用e.printStackTrace()

的默认处理程序

现在NullPointerException是运行时异常,它基本上指向代码中的某些逻辑谬误。有时你may想抓住它。但作为一个thumbrule:

  • 不捕获运行时异常,它们通常指向逻辑错误
  • 不要留下一个空的catch块,你会丢失堆栈跟踪和异常,如果你抓住它但不采取行动。
  • 不要只是将堆栈跟踪打印到标准输出,而是将其记录到某处以备后续分析(如果需要)

答案 5 :(得分:1)

约定是避免捕获任何类型的运行时异常。原因是:可怕的出了问题,作为开发人员,你应该热衷于在30分钟前修复它。

事实上,

NullPointerExceptionRuntimeException的子类。与其他父级为RuntimeException的类一样,它们被视为未选中 - 因此即使代码生成NPE,代码也会编译:

// This would appropriately return "false", if the order were reversed 
// and something was empty.
public boolean doSomething(String something) {
    String hello = null;
    if(hello.equals(something)) {
        System.out.println("Wow");
    }
}

明确地捕获任何类型的运行时异常表示您希望在程序过程中发生可怕的坏事,并希望掩盖这一事实。

至于catch块体内的内容......可能是由IDE自动生成的。如果您没有将异常传播到应用程序的更高级别,那么您需要获得有用的上下文来说明程序爆炸的原因。

如果你没有在catch区块中放置任何东西,它会被忽略 - 这是错误

答案 6 :(得分:1)

通常NullPointerException表示API使用中的逻辑错误或缺少exepcted值。这就是为什么建议把它扔掉。但是你可以抓住它。实际上

  

我建议您在想要给出默认值时抓住   传递的空参数。

例如:

您希望超时值可配置,并且您希望用户必须通过它。但是,用户忘记传递它。在这种情况下,您可以捕获NPE并记录您正在使用参数的默认值的警告。

答案 7 :(得分:1)

NullPointerException是代码中的一种情况,您尝试访问/修改尚未初始化的对象。

它实质上意味着对象引用变量没有指向任何地方,并且没有引用任何内容或“null”。

effective java中的

Joshua bloch 说:

  

“可以说,所有错误的方法调用都归结为非法   参数或非法状态,但标准使用其他例外   对于某些非法论点和国家。如果来电者通过   在禁止空值的某些参数中为null,   约定规定抛出NullPointerException而不是   抛出:IllegalArgumentException“。

因此,如果您必须在代码中的某些位置允许NullPointerException,那么请确保您提供的信息比通常情况更多。

从这里了解有关有效NPE处理的更多信息:

答案 8 :(得分:0)

强大的,以用户为中心的代码应该尝试捕获这样的“致命错误”,如果有什么事情可以做到这一点。您可以重试尝试的函数调用。也许这个空例外是由于某些瞬态。真正不应该抛出异常并且应该针对瞬态故障编码更好的东西,但是,这不应该阻止最终用户获得他想要的东西,而不是放弃可能是冗长的有状态表单输入过程,并且仅在最后一个完全无关紧要的步骤,比如输入用户的电子邮件首选项,或者出现空指针错误,并删除用户现有的输入。

答案 9 :(得分:-1)

您不必一直实际e.printstacktrace(),您可以使用记录器或System.out.println()

指定您自己的正文消息