在Java中编写"异常驱动开发"的性能成本?

cor*_*ath 14 java performance exception-handling exception overhead

通过在Java中创建,抛出和捕获异常,是否有任何性能成本?

我计划将"异常驱动的开发"添加到一个更大的项目中.我想设计自己的异常并将它们包含在我的方法中,迫使开发人员捕获并做适当的工作.

例如,如果您有一种方法可以根据名称从数据库中获取用户.

public User getUser(String name);
Run Code Online (Sandbox Code Playgroud)

但是,用户可能为空,并且在使用用户的公共方法之前忘记检查此操作是很常见的.

User user = getUser("adam");

int age = user.getAge();
Run Code Online (Sandbox Code Playgroud)

这将导致NullPointerException和崩溃.但是,如果我在返回user-object之前进行了检查,如果它为null并抛出'UserIsNullException':

public User getUser(String name) throws UserIsNullException;
Run Code Online (Sandbox Code Playgroud)

我强迫实施者思考并采取行动:

try {

    User user = getUser("adam");

    int age = user.getAge();

}catch( UserIsNullException e) {

}
Run Code Online (Sandbox Code Playgroud)

它使代码更安全地发生意外崩溃并消除更多错误.假设该网站每小时有数百名访问者,这种设计模式几乎无处不在.

这种设计方法将如何影响性能?这些好处是否会超过成本,还是只是简单的编码?

谢谢你的帮助!

UPDATE!要清楚,我的注意力不是包装NullPointerException,正如我的例子所暗示的那样.目标是强制实施者编写一个try/catch,从而避免真正崩溃的头痛:

user == null

被遗忘了.问题涉及比较这两种设计模型:

int age;

try {

User user = getUser("adam");

age = user.getAge();

}catch( UserIsNullException e) {

age = 0;

}
Run Code Online (Sandbox Code Playgroud)

与:

int age;

User user = getUser("adam");

if( user != null ) {
age = user.getAge();
} else {
age = 0;
}
Run Code Online (Sandbox Code Playgroud)

Wil*_*ill 8

"这将导致NullPointerException和崩溃."

NullPointerException是一个异常,可以在catch中捕获.

因此,除非增加代码的清晰度,否则额外的异常是多余的.在这种情况下,它确实没有.实际上,您刚刚采用了未经检查的程序员错误异常并将其提升为已检查的异常.

为程序员的错误创建一个已检查的异常实际上意味着你的代码必须明确地处理程序员引入错误的可能性.

  • 它消除了清晰度,因为程序员被迫在任何地方放置试图捕获新的异常,在另一种情况下他们只有代码解决问题而不是代码检查程序员没有搞砸了. (9认同)
  • @Chris:传播SQLException更糟糕恕我直言,因为它暴露了你的DAO实现的内部.如果我决定切换到使用其他存储机制怎么办?将SQLException转换为特定于您的应用程序的业务相关异常要好得多. (4认同)
  • @corgrath - 另一方面,过度使用已检查的异常也会导致编码错误,例如捕获异常或声明抛出异常. (3认同)

Bil*_*ard 8

抛出异常会有性能损失,但这通常是可以接受的,因为异常处理代码仅在特殊情况下执行.如果你开始使用流量控制的例外,那么你就会转向通常的情况并使它们成为预期的行为.我强烈建议不要这样做.


Nat*_*hes 7

有性能成本,但这不是主要问题.你真的希望你的代码充满了像你的例子一样的catch块吗?那太糟了.

通常,Web应用程序有一个主要的异常处理程序,其中所有未被捕获的内容都会结束,至少在处理错误时会以干净的方式切断处理.随着所有这些异常捕获,您的流程将会像落下几层楼梯一样.

一些例外是您期望和可以处理的非常特殊的情况.但是,一般情况下,当出现意外或无法控制的错误时会弹出异常.从那时起,您的对象可能处于错误状态,因为后续步骤将期望由于异常而未发生的事情,并且尝试继续将仅触发更多异常.让意外的异常消失会更好,因此它们可以被中央处理程序捕获.


Chr*_*ung 5

就个人而言,我认为这是一个糟糕而令人讨厌的想法.

鼓励人们检查空值的常用方法是使用注释@Nullable(和相反的注释@NotNull,对于保证返回非空值的函数).通过在参数上设置类似的注释(以便设置参数期望),当代码没有进行足够的检查时,质量IDE和错误检查器(如FindBugs)可以生成所有必要的警告.

JSR-305中提供了这些注释,显然还有一个参考实现.


就性能而言,创建异常是一个昂贵的部分(我已经读过它是由于堆栈跟踪填充等).抛出异常是便宜的,并且是JRuby中使用的控制转移技术.

  • Scala还使用它来模拟Java中的'break'关键字(请参阅Breaks.scala和NoStackTrace.scala:http://lampsvn.epfl.ch/trac/scala/browser/scala/trunk/src/library/scala/ util/control) (2认同)

Esk*_*sko 5

一般来说,Java中的异常非常昂贵,应该用于流量控制!

例外的一点是描述一些特殊的东西,例如NumberIsZeroException并不是那么特别,而PlanesWingsJustDetachedException显然是非常特殊的东西.如果在您的软件中用户是null因为数据损坏或ID号与任何类似的东西都不匹配,那么就可以使用例外.

什么例外也导致与"快乐道路"的分歧.虽然这不适用于您的示例代码,但有时使用Null Object而不是返回plain 非常有用null.


KLE*_*KLE 4

构建堆栈跟踪相当于大约一千条基本指令。它有一定的成本。


即使您不问,很多人也可能会告诉您您设想的方法似乎不太有吸引力......:-(

您强加给调用者的代码非常丑陋,难以编写和维护。

为了更具体并尽量让大家理解,我将尝试强调几点。

  • 其他开发人员负责检查从您的 API 收到的用户的无效性(如果文档明确说明)。他的代码会定期在内部执行此类检查,因此他也可以在调用您的 API 后执行此操作。更重要的是,这将使他的代码具有一定的同质性。

  • 当重用现有异常是适当的时候,它比创建自己的异常要好得多。例如,如果调用 API 并请求不存在的用户时出错,则可以抛出 IllegalArgumentException。