异常与断言

时间:2009-08-14 06:20:39

标签: java assert

Java异常处理和使用assert条件之间有什么区别?

众所周知,Assert有两种类型。但我们什么时候应该使用assert关键字?

10 个答案:

答案 0 :(得分:83)

在代码中使用断言进行内部逻辑检查,在正常代码控制之外使用错误条件的正常异常。

不要忘记断言可以打开和关闭 - 如果你关心参数验证之类的东西,那么应该使用异常来明确。 (但是,您可以选择使用断言对私有方法执行参数验证,理由是此时违规是由内部错误而非外部错误引起的。)

或者,对所有事情都使用例外是完全合理的(IMO)。我个人根本不使用断言,但这在某种程度上是个人偏好的问题。 (当然可以存在支持和反对断言的客观论据,但它并不足以明确地删除偏好。)

答案 1 :(得分:24)

Java断言建立在Java异常和异常处理之上。实际上,当Java断言失败时,结果是AssertionError异常,可以像任何其他Java异常一样被捕获。异常和断言之间的主要区别是:

  • 断言意图仅用作检测编程错误的手段,即bug。相反,异常可以指示其他类型的错误或“异常”条件;例如用户输入无效,文件丢失,堆满等等。
  • Java语言以assert语句的形式为断言提供语法支持。比较以下内容:

    if (x != y) {
         throw new SomeException("x != y");
    }
    
    assert x != y;
    
  • 最重要的是,Java允许您在启动JVM时全局或单个类启用或禁用断言检查。

注意:有人说你应该始终运行生产代码并关闭断言检查。作为一揽子声明,我倾向于不同意这一点。如果您的生产代码已知是稳定的并且您需要从中挤出最后一点性能,那么关闭断言是好的。但是,如果(例如)10%的性能损失不是一个真正的问题,如果备选方案继续并损坏我的数据库,我宁愿让应用程序死于断言错误。

@MarioOrtegón因此评论道:

  

“关闭”是因为断言可用于通过将其实现与众所周知但缓慢的算法进行比较来验证优化算法的结果。因此,在开发中,可以调用O(N^3)方法来断言O(log N)算法按预期工作。但这是你不想要的东西。

您是否认为在生产中关闭断言良好做法,在启用时编写对性能有重大影响的断言肯定是不良做法 。为什么?因为这意味着您不再可以选择在生产中启用断言(跟踪问题)或在压力/容量测试中启用断言。在我看来,如果你需要进行O(N^3)前/后条件测试,你应该在单元测试中进行。

答案 2 :(得分:6)

异常是一种检查实现是否正在执行而没有任何预期或意外错误的机制。因此,我们看到异常基本上用于处理应用程序执行期间不可预见的条件,从而以更好的方式使用异常,因此有效地使用异常会产生强大的应用程序。

断言绝不应成为应用程序某些功能实现的一部分。它们只应用于验证假设 - 只是为了确保在设计解决方案时我们所假设的内容在实际中也是有效的。

参考:http://geekexplains.blogspot.com/2008/06/asserions-in-java-assertions-vs.html

答案 3 :(得分:5)

断言与异常非常相似,实际上就像异常一样,它们会标记一个问题,但与异常不同 - 它们不会建议任何替代执行路径,但只会失败。 为什么要使用断言,如果你可以做同样的事情,还有更多的例外?

当问题不能解决时使用它们,实际上不应该在第一个地方发生。这听起来很奇怪:我们不想保护我们的代码免受所有潜在问题的影响吗?通常是的。但有一种情况我们没有。这种情况称为“按合同设计”。

假设您正在为银行撰写申请表。作为开发人员,您无法支持所有可能的财务状况。因此,在开始编码之前,您会从银行获得一个规范,该规范为您提供此应用程序应支持的有效范围。所以你的申请是由合同设计的(根据银行的规范)。该合同将定义应始终为真,以使您的应用程序正常工作的基本原则。这些基本原则被称为“不变量”(因为它们不能改变)。合同设计简化了您作为开发人员的生活 - 您只负责支持合同中规定的工作范围 检查代码中这些不变量的值很重要,但是不应该将它们检查为异常并尝试解决它们。如果他们错了 - 你必须失败,因为投入没有履行他们的合同义务。

有趣的是:如果你没有把断言置于关键位置并且不变量变得无效 - 你的代码无论如何都会失败。你只是不知道为什么。总而言之 - 断言用于验证不变量。它们与“按合同设计”原则齐头并进。使用它们来调试问题,这就是它们在生产中被关闭的原因。

另一个用例:如果你依赖的是一个你不完全信任的外部库 - 你可能想在调用时使用assert语句。

有些人还使用断言作为异常的快速和脏的替代品(因为它很容易做到),但从概念上讲,这不是正确的事情。

答案 4 :(得分:4)

善用Assert的例子:

assert flibbles.count() < 1000000; // too many flibbles indicate something is awry
log.warning("flibble count reached " + flibbles.count()); // log in production as early warning

我个人认为,当您知道某些内容超出期望的限制时,Assert应该,但您可以确定它的继续是合理安全的。在所有其他情况下(随意指出我没有想到的情况)使用例外来快速失败。

对我来说,关键的权衡是你是想要关闭带有异常的实时/生产系统以避免损坏并使故障排除更容易,或者是否遇到过永远不允许在测试/调试中不被注意的情况版本,但可以允许继续生产(当然记录警告)。

比照http://c2.com/cgi/wiki?FailFast另请参阅此答案的c#副本:Debug.Assert vs. Specific Thrown Exceptions

答案 5 :(得分:1)

断言仅用于调试目的,不应发生触发条件(不应该出现空指针等)

异常是针对可能经常发生的特殊系统事件:FileNotFound,ConnectionToServerLost等。

答案 6 :(得分:1)

断言和异常处理都可以确保程序正确性并避免逻辑错误,

但是断言可以按照程序员的意愿启用和禁用,

在编译器中,如果您使用“ java -ea JavaFileName”或“ java -enableasserations JavaFileName” 您可以使用断言进行编译

如果程序员不需要它,则可以禁用断言。“ java -da JavaFileName”或“ java -disableasserations JavaFileName”。

此功能不在异常处理中

答案 7 :(得分:0)

断言用于调试在运行时仅通过启用断言功能来检查所需的假设,而异常是检查在执行程序期间要检查的特定条件以防止程序终止。

答案 8 :(得分:0)

尽管,我已经将answer发布在se.stackexchange网站上,但仍然可以在此处发布。

使用了断言

  1. 当您想立即停止程序而不是进入不需要的状态时。这通常与Fail-fast [1] 系统设计的原理有关。

  2. 在第一个意外情况下可能发生级联故障(即在微服务中)的某些可能性时,可能导致应用程序进入严重的不一致或不可恢复状态。

  3. 仅在调试期间要检测系统中的错误时。如果语言支持,您可能想在生产中禁用它们。

  4. 当您已经知道由于内部未实现和外部系统(即调用方)而引起意外情况时,您将无法控制不需要的状态。

使用了异常

  1. 当您知道由于外部系统故障(例如,错误的参数,缺少资源等)而导致出现意外情况时。

  2. 当您知道可以使用备用路径来备份条件时,可以保持应用程序功能质量不变(即,对于来自调用方或外部系统的具有适当参数的其他调用可能效果很好)。

  3. 当您要登录并让开发人员知道一些不需要的状态时,没什么大不了的。

注意:“使用断言的次数越多,获得的系统越健壮”。相比之下,“您使用和处理异常的次数越多,您获得的系统就越有弹性”。


[1]快速失败-在系统设计中,快速失败系统是一种立即在其界面上报告任何可能表示失败的情况的系统。快速故障系统通常旨在停止正常运行,而不是尝试继续可能存在缺陷的过程。这样的设计通常会在操作的多个点检查系统的状态,因此可以及早发现任何故障。快速故障模块的职责是检测错误,然后让系统的下一个最高级别处理错误。

答案 9 :(得分:0)

将异常用于希望发生的条件,而用户断言用于不应发生的条件。

通常,在现实世界的项目中,您希望异常包含不应该发生的条件,并防止断言变为事实。毕竟,您希望程序能够运行并且永远不会进入无效状态,因此您应该使用异常和其他技术来确保程序永远不会进入这种状态。但是,有时候,有多种方法可以使断言变为真实,并且您在那时只能预料到其中的几种方法。因此,通过断言获得这些“硬约束”可以确保,如果将来程序处于您未曾预料到的不可用状态,并且该状态不应该发生,则该程序将停止运行,并且您必须调查这是如何发生的。 / p>

换句话说,异常检查无效的输入数据,而断言充当检测代码中错误的机制。