在java中尝试/ catch vs null检查

有时我面对我必须写一段这样的代码(通常它有更多的嵌套if和更复杂的结构,但是对于这个例子来说)

public void printIt(Object1 a){ if (a!=null){ SubObject b= a.getB(); if (b!=null){ SubObject2 c=b.getC(); if(c!=null){ c.print(); } } } } 

当我不需要知道什么是失败的,如果什么是空的什么都不做,一种方法可能是

 public void printIt(Object1 a){ try{ a.getB().getC().print(); }catch (NullPointerException e) { } } 

第二种forms如性能或其他类型的问题是否有问题?

谢谢

exception版本(类似于使用Groovy的安全导航操作符的链?. )使得很容易采用得墨忒耳法则(或者我称之为Demeter的强烈建议),并使它成为你夜晚的玩物。

类似地,深度嵌套的if -statements导致难以阅读的代码,并且在其下面,存在相同的“违规”,并且这种方法的圈复杂度很高。

 public void printIt(Object1 a) { if (null == a) { return; } SubObject b = a.getB(); if (null == b) { return; } SubObject2 c = b.getC(); if (null == c) { return; } c.print(); } 

我宁愿在合适的地方看到LAM(小辅助方法)来封装检查,并且几乎完全消除了对问题的需求。

是。 第二个版本会有糟糕的表现。

不要将exception用于正常控制流程。 有效的Java项目57:仅在特殊情况下使用exception。

== ==更新

甚至忽略了性能问题(根据我的基准测试 ,exception比以前更快,但不如简单的检查那么快),对于像这样的标准程序流使用exception真的很臭。 JVM字节码具有特殊的优化function,即使在if语句中也可以进行空检查。 第一个代码示例是非常优选的。

 public void printIt(Object1 a){ if(a==null){ throw new IllegatArgumentException("a was null, but this is not allowed here."), } [...] 

快速失败并努力失败。 如果shoud不为null,则抛出exception。 这将使您的代码更加稳定和可靠。

所以,如果我必须在你的a)和你的b)之间做出决定,我会选择a)。 但是如果a不能为空,那么你会隐藏错误情况。

第二个版本的最糟糕的部分是,当一个NPE发生在getB()内部时, getC()它将被默默地忽略。 如前所述,例外情况适用于特殊情况。

使用exception在性能方面总是一个坏主意,无论机制过去和现在的速度有多慢。 每当抛出exception时,将展开完整堆栈以创建堆栈跟踪。 因此,就像Lois Wasserman所说的那样,你不应该依赖它们来进行(常规)程序流程,而是针对特殊情况。

嵌套ifs不是美的定义,但会让你能够打印其他信息,如’B is null’等。

答案是使用版本A.

通常认为将exception用于流量控制是“糟糕的设计”。 例外情况是“特殊”,尤其是使用空检查完全可以避免的NPE。 此外,使用空检查,您可以告诉(即记录) 哪个术语为空(您不会知道null与版本B的位置)。

请注意,性能不再是抛出exception的问题(例如,堆栈跟踪仅在您使用时才构建)。 这是一个干净的代码问题。

但是,在某些情况下,使用流控制的exception是不可避免的,例如从SimpleDateFormat.parse()抛出的exception,因为在调用之前没有合理的方法告诉您输入是不可解析的。

绝对是(a)但是你应该重新构造方法以避免嵌套前面答案中提到的if语句。 exception不是它们曾经的性能损失 ,但仍然比检查null慢得多,并且永远不应该像这样用于程序流控制。 如果对象可以为null,则应检查它,但如果不允许,则应在分配对象引用的位置快速失败。 在许多情况下,您可以使用默认实现(空列表是一个很好的示例)以完全避免空值,从而产生更清晰的代码。 尽可能避免使用空值。

代码永远不应包含未经检查的exception的exception处理程序。 应始终将null检查用于可能为null的对象引用。

异灵

如果迁移到Java 8,则可以使用Optional和Lambdas。 首先,您需要重写您的类以返回每种类型的Optional

 class Object1 { private SubObject b; Optional getB() { return Optional.ofNullable(b); } } class SubObject { private SubObject2 c; Optional getC() { return Optional.ofNullable(c); } } class SubObject2 { @Override public String toString() { return "to be printed"; } } 

现在,您可以以简洁的方式链接调用而不会有NullPointerExceptions的风险:

 a.getB() .flatMap(SubObject::getC) .ifPresent(System.out::println); 

请参阅Oracle的文章厌倦了空指针exception? 考虑使用Java SE 8的可选项! 了解更多信息。

使用Java 8可选:

 Optional.ofNullable(a) .map(Object1::getB) .map(SubObject::getC) .ifPresent(Object2::print);