什么时候可以捕获NullPointerException?
有效的java建议我们不要catch
NullPointerException
。 总是对的吗?
在很多捕获NullPointerException
情况下,catch body只调用printStackTrace()
。
如果我没有捕获NullPointerException
并调用printStackTrace()
,我如何检查发生exception
的位置?
而且如果我捕获NullPointerException
并且catch
体是空的,那么我们当时无法得到任何堆栈信息,是吗?
UPDATE
我分析了google android源码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和外部通过抛出其他exception来处理NPE。
但框架通常通过返回null或其他值来处理。
-
你是否认为将其他例外投入捕获体中并不坏或处理好?
-
如果NPE出现在更高级别(例如应用程序)并且开发人员确认exception并不重要,因为应用程序是完全独立的,我可以接受我们的开发人员通过捕获来忽略NPE吗?
还有一件事,
我可以得出结论,谷歌android框架源代码可能在RuntimeException
方面有些不稳定吗?
有效的java建议我们不要捕获NullPointerException。 总是对的吗?
几乎在所有情况下都是正确的。
NullPointerException
通常是一个bug的结果; 即您的应用程序在未预料到的情况下遇到null
对象引用,然后尝试使用它。 在这种情况下,由于您(程序员)没有预料到null
,因此几乎不可能知道尝试恢复是否安全,和/或知道可能需要什么“补救”。 因此,最好的办法是让NPE传播到基本级别,然后将其视为通用错误。 (在“网络服务”应用程序中,可能适合返回“服务错误”响应,并尝试继续。)
另一种情况是您(程序员)预期可能会传递null
。 在这种情况下,最好的策略是(几乎总是) 在尝试使用它之前显式测试null
,从而避免 NPE ……以及处理它的需要。 有两个原因:
-
exception处理通常很昂贵。 实际上,它比测试
null
要贵许多个数量级 。 -
如果您允许预期的NPE发生然后捕获它,您也有可能捕获其他意外的 NPE ……并且错误地处理它们。
请注意,我通过说“几乎总是”来certificate上述内容。 从理论上讲,有一种情况可能会使null
显式测试使代码混乱,以至于至少值得考虑允许NPE发生。 但是,仍然存在意外的NPE的可能性……取决于代码。 所以这种方法总是很脆弱。
(FWIW – 我从来没有遇到过这个好主意的真实案例……)
在很多捕获NullPointerException的情况下,catch body只调用printStackTrace()。
这可能是糟糕的代码。 无所事事很少是从NPE中恢复的正确方法。
如果我没有捕获NullPointerException并调用printStackTrace(),我如何检查发生exception的位置?
您让NPE传播到基准级别。 在那里你捕获并打印(或记录) 所有未处理exception的堆栈跟踪,然后挽救或尝试恢复……如果这是可行的。
而且如果我捕获NullPointerException并且catch体是空的,那么我们当时无法得到任何堆栈信息,是吗?
永远不要这样做! 它被称为“挤压”并且很危险 。 (特别是因为,正如我上面所解释的那样,NPE可能是由于你/你的代码没有预料到的。)
不,如果你这样做,你就无法获得堆栈跟踪。 它消失了。
跟进
我对“避免NPEs”的一些一般策略没有太多的信任/信心1 。 例如这样的东西:
return (someObject != null) ? someObject.toString() : "";
总是让我怀疑程序员没有考虑这个问题 。 为什么someObject
是null
?
NPE是由于在您不期望它的位置存在null
而引起的。 因此,NPE通常是问题的症状 ,而不是实际问题本身。 在我看来,NPE不是一个值得避免的东西。 相反,您应该使用NPE来查找并修复意外null
的根本原因 。 像上面那样避免NPE的代码妨碍了这个目标。
所以我更喜欢/建议在意外的地方避免null
值的策略。
-
确保每个引用字段都被初始化为非空值…除非
null
是有意义的值。 -
尽量避免将
null
作为有意义的值,特别是如果有替代方法。 例如,一个空字符串,一个零长度数组,一个空集合,一个表示“未定义”或其他的可分辨实例。 或者,对于Java 8及更高版本,请使用Optional
。 -
不要将
null
作为错误或特殊情况的指示返回。 (抛出exception或返回一个值。) -
早期检查未预料到的
null
值(例如null
参数),并尽快抛出NPE。 -
在
null
参数或结果合法的少数几个地方,请确保您的javadocs清楚明确地记录了这一点。 如果没有文档,那么暗示应该是不允许null
并且不会返回。
无论你在哪里获得NPE,都要确保找到并修复问题的真正根源……而不仅仅是抛出exception的特定语句。
1 – 了解标准Java API中使用null
(或滥用)作为返回值的位置是有价值的。 例如, Class.getResourceAsStream(...)
或HttpRequest.getParam(...)
。 这些“避免NPE的建议”文件在指出这些陷阱方面非常有用。
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") }
通常建议不应该捕获NullPointerException
并让它由JVM处理。
但这完全取决于,例如:如果您有像List
这里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){ }
你绝对不应该使用空捕获块而没有真正重要的原因。 通常没有这样的理由。
通常,检查变量是否为空或捕获NPE以及更合适的原因是好的。 例如,如果方法getCar()
返回null
,则可能在尝试调用car
某个方法时捕获NPE,或者检查汽车是否为null
并抛出RuntimeException
(但仅在情况确实exception时)。 因此,您可以使错误更具抽象性,更易于理解。
抛出exception时,无论是exception,jvm都会查找最近的handler
(catch块)。 如果在当前方法中找不到它,它会在调用代码中查找处理程序,依此类推。 基本上,从下到上搜索整个calling stack
以寻找可以处理所述exception的catch块。 如果没有找到处理程序,jvm使用调用e.printStackTrace()
的默认处理程序
现在NullPointerException
是一个运行时exception,它基本上指向代码中的一些逻辑谬误。 有时你may
想抓住它。 但作为一个thumbrule:
- 不捕获运行时exception,它们通常指向逻辑错误
- 不要留下一个空的catch块,你会失去堆栈跟踪和exception,如果你抓住它但不采取行动。
- 不要只是将堆栈跟踪打印到标准输出,而是将其记录到某个地方以供以后分析(如果需要)
惯例是避免捕获任何类型的运行时exception。 原因是:一些可怕的事情出了问题,作为开发人员,你应该热衷于在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"); } }
明确地捕获任何类型的运行时exception表明您期望在程序过程中发生可怕的坏事,并且您希望掩盖这一事实。
至于catch
块体内的内容……可能是由IDE自动生成的。 如果您没有将exception传播到应用程序的更高级别,那么您需要获得有用的上下文来说明程序爆炸的原因。
如果你没有在catch块中放入任何东西,它会被忽略 – 这很糟糕 。
通常NullPointerException
表示API使用中的逻辑错误或缺少exepcted值。 这就是为什么建议把它扔掉。 但是你可以抓住它。 几乎
当你想为传递的null参数给出默认值时,我建议你抓住。
例如 :
您期望超时值可配置,并且您希望用户必须通过它。 但是,用户忘记传递它。 在这种情况下,您可以捕获NPE并记录您正在使用参数的默认值的警告。
NullPointerException
是代码中的一种情况,您尝试访问/修改尚未初始化的对象。
它本质上意味着对象引用变量不指向任何地方并且不引用任何内容或“null”。
effective java
Joshua bloch说:
“可以说,所有错误的方法调用归结为非法论证或非法状态,但其他例外标准地用于某些类型的非法论证和状态。 如果调用者在某些禁止空值的参数中传递null,则约定表示抛出NullPointerException而不是IllegalArgumentException。“
因此,如果您必须在代码中的某些位置允许NullPointerException
,那么请确保使它们通常比它们更具信息性。
从这里了解有关有效NPE处理的更多信息:
强大的,以用户为中心的代码应该尝试捕获这样的“致命错误”,如果有什么事情可以做到这一点。 您可以重试尝试的函数调用。 也许这个空的例外是由于某些瞬态。 真正不应该抛出exception并且应该针对瞬态故障编码更好的东西,但是,这不应该阻止最终用户获得他想要的东西,而不是放弃可能是冗长的有状态表单输入过程,并且仅在最后一个完全无关紧要的步骤,比如输入用户的电子邮件首选项,或者出现空指针错误,并删除用户的现有输入。
您不必一直实际执行e.printstacktrace()
,您可以使用logger或System.out.println()
指定自己的正文消息