何时使用断言以及何时使用异常


121

在大多数情况下,我将使用异常检查代码中的条件,我想知道何时才是使用断言的适当时间?

例如,

Group group=null;
try{
    group = service().getGroup("abc");
}catch(Exception e){
    //I dont log error because I know whenever error occur mean group not found
}

if(group !=null)
{
    //do something
}

您能指出断言如何适合这里吗?我应该使用断言吗?

似乎我从不在生产代码中使用断言,而仅在单元测试中看到断言。我确实知道,在大多数情况下,我可以像上面那样使用异常来进行检查,但是我想知道“专业”地执行异常的适当方法。

Answers:


81

断言应用于检查不应发生的事情,而异常应用于检查可能发生的事情。

例如,一个函数可能会被0除,因此应使用异常,但可以使用一个断言来检查硬盘驱动器突然消失。

断言将阻止程序运行,但是异常将使程序继续运行。

请注意,这if(group != null)不是断言,而只是有条件的。


3
“可以使用一个断言来检查硬盘驱动器是否突然消失”-我会说这是不正确的:为什么要在开发过程中而不是在生产中(通常禁用断言时)处理此问题?
Herman 2013年

71
关于硬盘的评论是错误的。断言用于检查代码逻辑中的错误。永远不要使用它们来检查您无法控制的内容。请记住,如果断言失败,则意味着您的代码是错误的
伊恩·戈德比

1
@Marius您的条件可以用这样的断言代替:assert group!= null
IgorGanapolsky 2014年

1
由于硬盘示例与您自己的理念相矛盾,因此被否决了。硬盘“消失”(从代码的角度来看)实际上可能在现实中发生-不管多么不可能。就像@IanGoldby所说的那样,断言应完全取决于代码控制的事物。
Vicky Chijwani,

如果格雷格里·帕科斯(Gregory Pakosz)发布了更好的答案,请阅读该帖子。
ormurin

169

我会不高兴(列表可能不完整,并且太长了,无法放入评论中),我会说:

  • 检查传递给公共或受保护的方法和构造函数的参数时使用异常
  • 与用户交互时,或者当您期望客户端代码从异常情况中恢复时,请使用异常
  • 使用异常来解决可能发生的问题
  • 在检查私有/内部代码的前置条件,后置条件和不变量时使用断言
  • 使用断言向您自己或您的开发团队提供反馈
  • 在检查不太可能发生的事情时使用断言,否则意味着您的应用程序中存在严重缺陷
  • 使用断言陈述您(应该)知道是真实的事情

换句话说,异常处理应用程序的健壮性,而断言处理应用程序的正确性。

断言的编写成本很低,您几乎可以在任何地方使用它们,而我使用的是这样的经验法则:断言语句看起来越愚蠢,它就越有价值,并且嵌入的信息越多。在调试行为不正确的程序时,您一定会根据自己的经验检查更明显的故障可能性。然后,您将检查根本不会发生的问题:这正是断言有很大帮助并节省时间的时候。


53
我喜欢您这样说的方式:异常解决应用程序的健壮性,而断言解决应用程序的正确性
杜德利先生(M. Dudley)


而且,如果您正巧寻找C ++自定义断言库,我发布了github.com/gpakosz/Assert
Gregory Pakosz,2015年


15

作为基本规则:

  • 使用断言进行内部一致性检查,如果有人将其关闭则根本不重要。(请注意,该java命令默认情况下会关闭所有断言。)
  • 使用常规测试进行任何形式的检查,哪些不应该关闭。这包括防御性检查,以防止错误引起的潜在损害,以及任何验证数据/请求/由用户或外部服务提供的任何内容。

您问题中的以下代码风格不好可能有错误

try {
    group = service().getGroup("abc");
} catch (Exception e) {
    //i dont log error because i know whenever error occur mean group not found
}

问题是您不知道异常意味着未找到该组。service()调用也有可能引发异常,或者它返回null后导致NullPointerException

当您捕获“预期”异常时,您应该捕获预期的异常。通过捕获java.lang.Exception(尤其是不记录日志),您将更加难以诊断/调试问题,并可能使应用程序遭受更大的损害。


4

好吧,回到Microsoft时,建议是在公开提供的所有API中都抛出Exception,并在对内部代码所做的各种假设中使用Asserts。定义有点松散,但我想这取决于每个开发人员。

关于异常的用法,顾名思义,它们的用法应该是特殊的,因此对于上面显示的代码,如果不存在任何服务,则getGroup调用应返回null。仅当网络链接断开或类似情况时,才应发生异常。

我想得出的结论是,每个应用程序定义断言与异常的界限都需要由开发团队来负责。


恕我直言,这种建议的问题是,只要API的公共部分和私有部分之间的边界很固定,就可以了。如果您正在开发新代码,则该边界通常会很
不稳定

你是对的。这只是一个准则,但最终归结于程序员的敏感性。我认为这些没有最终定义线,因此我想您只是通过阅读不同代码的负载来遵循您认为正确的方法。

3

根据此文档http://docs.oracle.com/javase/6/docs/technotes/guides/language/assert.html#design-faq-general,“ assert语句适用于非公共前置条件,后置条件和类不变式公共先决条件检查仍应通过检查内部方法来执行,这些方法会导致特别记录的异常,例如IllegalArgumentException和IllegalStateException。”

如果您想进一步了解前置条件,后置条件和类不变性,请查看以下文档:http : //docs.oracle.com/javase/6/docs/technotes/guides/language/assert.html#usage-conditions。它还包含断言用法的示例。


1

测试null只会捕获导致问题的null,而按您的方式进行try / catch则会捕获任何错误。

从广义上讲,try / catch较为安全,但速度稍慢,因此必须小心捕捉所有可能发生的错误。因此,我想说说使用try / catch-有一天getGroup代码可能会更改,而您可能只需要更大的网络。


1

您可以在使用时记住这个简单的区别。异常将用于检查预期的和意外的错误,称为已检查和未检查的错误,而断言主要用于在运行时进行调试,以查看假设是否得到验证。


1

我承认你的问题让我有些困惑。当不满足断言条件时,将引发异常。令人困惑的是,这称为AssertionError。请注意,它是未经检查的,例如(在非常类似的情况下引发的)IllegalArgumentException

因此在Java中使用断言

  1. 是写条件/抛出块的更简洁的方法
  2. 允许您通过JVM参数打开/关闭这些检查。通常,我会一直不停地进行这些检查,除非它们影响运行时性能或有类似的损失。

AssertionError是Error的子类,而不是RuntimeException。
Stephen C

啊。当然。我在想检查/未检查。现在已更正
Brian Agnew

它解释了什么是断言(从一个有争议的角度来看),但是没有解释何时确切地使用它们。
Karl Richter

1

请参见以下链接的Sun文档的6.1.2节(断言与其他错误代码)。

http://www.oracle.com/technetwork/articles/javase/javapch06.pdf

该文档提供了有关何时使用断言的最佳建议。引用文档:

“一个好的经验法则是,对于异常情况,您应该使用断言。断言是处理和忘记您不希望遇到的条件或状态的最快方法。处理。”


0

不幸的是,断言可以被禁用。在生产中,当需要跟踪意外情况时,您需要获得所有帮助,因此断言会使其失去资格。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.