21

有效的 java 建议我们不应该catch NullPointerException. 总是正确的吗?

在很多情况下NullPointerException,catch 体只调用printStackTrace()

如果我不接NullPointerException电话printStackTrace(),我怎么能检查发生的地方exception

而且如果我抓到NullPointerException并且catchbody是空的,我们那时就无法获得任何堆栈信息,可以吗?

更新

我分析了google android源码AOSP4.2.2_r1.2中捕获RuntimeException的统计数据。

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

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 和 external 通过抛出其他异常来处理 NPE。

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

  1. 你认为在 catch-body 中抛出其他异常是不是不好或好的处理?

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

还有一件事情,

我可以断定google android框架源代码在某些方面可能有些不稳定RuntimeException吗?

4

10 回答 10

43

有效的 java 建议我们不应该捕获 NullPointerException。总是正确的吗?

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

NullPointerException通常是错误的结果;即,您的应用程序在未预料到的情况下遇到了null对象引用,然后尝试使用它。在这种情况下,由于您(程序员)没有预料到,几乎不可能知道尝试null恢复是否安全,和/或知道可能需要什么“补救”。所以最好的办法就是让 NPE 传播到基层,然后把它当作一个通用的 bug。(在“网络服务”应用程序中,返回“服务错误”响应并尝试继续可能是合适的。)

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

  • 异常处理通常很昂贵。实际上,它可能测试null.

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


请注意,我通过说“几乎总是”来限定上述内容。从理论上讲,有可能出现这样一种情况,即显式测试null会使您的代码变得如此混乱,以至于至少值得考虑允许 NPE 发生。但是,仍然存在意外 NPE 的可能性……取决于代码。所以这种方法总是潜在的脆弱。

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


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

那可能是糟糕的代码。什么都不做很少是从 NPE 中恢复的正确方法。

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

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

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

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

不,如果您这样做,您将无法获得堆栈跟踪。它不见了。


跟进

我对“避免 NPE” 1的一些一般策略不太信任/相信。例如像这样的东西:

return (someObject != null) ? someObject.toString() : "";

总是让我怀疑程序员没有考虑问题。为什么是someObjecta null

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

所以我更喜欢/推荐null在意想不到的地方避免价值的策略。

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

  • 尽量避免将其null作为有意义的值,尤其是在有替代方案的情况下。例如,一个空字符串、一个零长度数组、一个空集合、一个表示“未定义”的杰出实例或其他任何东西。或者,对于 Java 8 及更高版本,使用Optional.

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

  • 尽早检查未预料到的null值(例如参数),并尽早而不是稍后null抛出 NPE 。

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

无论您在哪里获得 NPE,请确保您找到并修复问题的真正根源……而不仅仅是引发异常的特定语句。

1 - 了解标准 Java API 中null使用(或滥用)作为返回值的位置是有价值的。例如,Class.getResourceAsStream(...)HttpRequest.getParam(...)。那些“避免 NPE 的建议”文档在指出这些陷阱时很有用。

于 2013-08-16T05:37:14.220 回答
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")
}
于 2013-08-16T04:49:09.007 回答
2

一般建议不要抓到NullPointerException ,让JVM来处理。

但这一切都取决于,例如:如果你有类似的代码List<Student> listStudent = getAllStudent();

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

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

如果您不这样做,那么NullPointerException如果没有数据,就有可能发生这种情况。

所以更好的做法是检查 Nulls 但不要像处理它

try{ 

}catch(NullPointerException e){

}
于 2013-08-16T04:51:27.343 回答
1

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

NullPointerException实际上是 的子类RuntimeException。像其他具有 的父类的类一样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 块中放任何东西,它会被默默地忽略——这很糟糕

于 2013-08-16T05:10:29.307 回答
1

通常NullPointerException表示 API 使用中的逻辑错误或缺少预期值。这就是为什么建议把它扔回去的原因。但是,您可以抓住它。几乎

我建议您在想要为传递的空参数提供默认值时进行捕捉。

例如 :

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

于 2013-08-16T05:13:51.947 回答
1

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

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

约书亚布洛赫effective java

“可以说,所有错误的方法调用都归结为非法参数或非法状态,但其他例外通常用于某些类型的非法参数和状态。如果调用者在某个参数中传递了 null 值,而该参数被禁止为 null 值,则约定规定抛出 NullPointerException 而不是 IllegalArgumentException。”</p>

因此,如果您必须NullPointerException在代码中的某些地方允许,那么请确保您使它们提供更多信息,然后它们通常是。

从此处阅读有关有效 NPE 处理的更多信息:

于 2013-08-16T05:20:12.260 回答
1

如果没有非常重要的原因,您绝对不应该使用空的 catch 块。通常没有这样的原因。

通常最好检查变量是否为空或捕获 NPE 并找到更合适的理由。例如,如果一个方法getCar()返回null,您可能会在尝试调用 的某个方法时捕获 NPE car,或者检查汽车是否是null并抛出RuntimeException(但仅当情况确实异常时)。因此,您使错误更加抽象且更易于理解。

于 2013-08-16T04:51:17.037 回答
1

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

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

  • 不要捕获运行时异常,它们通常指向逻辑错误
  • 不要留下一个空的 catch 块,如果你捕获它但不对其采取行动,你将丢失堆栈跟踪和异常。
  • 不要只是将堆栈跟踪打印到标准输出,而是将其记录在某个地方以供以后分析,如果需要的话
于 2013-08-16T04:54:27.100 回答
0

健壮的、以用户为中心的代码应该尝试捕捉这样的“致命错误”,如果你可以做任何事情的话。您可以重试尝试的函数调用。也许这个空异常是由于一些暂时的。确实不应该抛出异常并且应该针对瞬态故障进行更好的编码,但是不应该阻止最终用户获得他想要的东西,而不是放弃可能是冗长的有状态表单输入过程,并且只在最后一个完全无关紧要的步骤上,例如输入用户的电子邮件首选项或发生空指针错误并丢弃用户现有输入的东西。

于 2013-08-21T20:17:04.667 回答
-1

您不必一直这样做e.printstacktrace(),您可以使用记录器或指定您自己的正文消息System.out.println()

于 2013-08-16T04:47:44.413 回答