assert
Java异常处理和使用条件有什么区别?
众所周知,Assert 有两种类型。但是我们什么时候应该使用assert
关键字呢?
对代码中的内部逻辑检查使用断言,对直接代码无法控制的错误情况使用正常异常。
不要忘记断言可以打开和关闭——如果你关心参数验证之类的事情,那应该使用异常明确。(但是,您可以选择使用断言对私有方法执行参数验证,理由是此时的违规是由于内部错误而不是外部错误。)
或者,对所有事情都使用例外是完全合理的(IMO)。我个人根本不使用断言,但这在某种程度上是个人喜好问题。(当然可以有支持和反对断言的客观论据,但它还不够明确,不能完全消除偏好。)
Java 断言建立在 Java 异常和异常处理之上。实际上,当 Java 断言失败时,结果是 AssertionError 异常,可以像任何其他 Java 异常一样被捕获。异常和断言之间的主要区别是:
Java 语言以语句的形式为断言提供语法支持assert
。比较以下内容:
if (x != y) {
throw new SomeException("x != y");
}
assert x != y;
最重要的是,Java 允许您在启动 JVM 时启用或禁用全局或单个类的断言检查。
注意:有人说您应该始终在关闭断言检查的情况下运行生产代码。我倾向于不同意这种笼统的说法。如果您的生产代码已知是稳定的,并且您需要从中挤出最后一点性能,那么关闭断言很好。但是,如果(比如说)10% 的性能损失不是一个真正的问题,如果替代方案是继续并损坏我的数据库,我宁愿让应用程序因断言错误而死掉。
@Mario Ortegón 如此评论:
“关闭”是因为断言可用于通过将优化算法的实现与众所周知但缓慢的算法进行比较来验证优化算法的结果。因此,在开发过程中,可以调用该
O(N^3)
方法来断言该O(log N)
算法按预期工作。但这是您在生产中不想要的东西。
无论您是否认为在生产中关闭断言是一种好习惯,编写启用后对性能有重大影响的断言绝对是不好的做法。为什么?因为这意味着您不再可以选择在生产(跟踪问题)或压力/容量测试中启用断言。在我看来,如果您需要进行O(N^3)
前置/后置条件测试,您应该在单元测试中进行。
异常是一种检查实现是否在没有任何预期或意外错误的情况下执行的机制。因此,我们看到异常基本上用于以更好的方式在应用程序执行期间甚至处理不可预见的条件,因此有效地使用异常会产生一个健壮的应用程序。
断言永远不应成为应用程序某些功能实现的一部分。它们应该只用于验证假设——只是为了确保我们在设计解决方案时所做的假设在实际中也是有效的。
参考: http: //geekexplains.blogspot.com/2008/06/asserions-in-java-assertions-vs.html
断言与异常非常相似,实际上就像异常一样,它们会标记问题,但与异常不同 - 它们不会建议任何替代执行路径,而只会失败。为什么要使用断言,如果你可以做同样的事情,加上更多的例外?
当问题不应该解决并且实际上不应该首先发生时使用它们。起初这听起来很奇怪:我们不想保护我们的代码免受所有潜在问题的影响吗?通常是的。但也有我们不这样做的情况。这个案例被称为:“合同设计”。
假设您正在为银行编写应用程序。作为开发人员,您不可能支持所有可能的财务状况。因此,在开始编码之前,您会从银行获得一份规范,该规范为您提供了该应用程序应支持的有效范围。因此,您的应用程序是根据合同设计的(根据银行的规范)。该合同将定义为使您的应用程序正常工作应始终正确的基本原则。这些基本原则被称为“不变量”(因为它们不能改变)。合同设计简化了您作为开发人员的生活 - 您只负责支持合同中定义的工作范围。
检查代码中这些不变量的值很重要,但不应将它们视为异常并尝试解决它们。如果他们错了——你一定会失败,因为输入没有履行他们的合同义务。
有趣的是:如果您不将断言放在关键位置并且不变量变得无效 - 您的代码无论如何都会失败。你只是不知道为什么。所以总结一下 - 断言用于验证不变量。它们与“按合同设计”原则密切相关。使用它们来调试问题,这就是它们在生产中被关闭的原因。
另一个用例:如果您依赖于您不完全信任的外部库 - 您可能希望在调用它时使用 assert 语句。
有些人还使用断言作为异常的快速而肮脏的替代品(因为它很容易做到),但从概念上讲,这不是正确的做法。
良好使用 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
虽然,我已经在 se.stackexchange 网站上发布了答案,但在这里发布可能仍然有帮助。
当您想立即停止程序而不是继续进入不需要的状态时。这通常与Fail-fast [1]系统设计的理念有关。
当第一个意外情况可能导致级联故障(即在微服务中)存在某些可能性时,这可能导致应用程序进入严重的不一致或不可恢复状态。
当您想在调试期间专门检测系统中的错误时。如果语言支持,您可能希望在生产中禁用它们。
当您已经知道由于您的内部错误实现和外部系统(即调用者)无法控制不需要的状态而出现意外情况时。
当您知道由于外部系统故障(即参数错误、资源不足等)而出现意外情况时。
当您知道可以使用替代路径备份条件时,保持应用程序功能质量保持不变(即,对于来自调用者或外部系统的具有适当参数的另一个调用可能会很好地工作)。
当您想记录并让开发人员知道一些不需要的状态但没什么大不了的时候。
注意: “你使用断言的次数越多,你得到的系统就越健壮”。相比之下,“您使用异常并处理它们的次数越多,您获得的系统就越有弹性”。
[ 1 ] 快速失败- 在系统设计中,快速失败系统是在其界面上立即报告任何可能指示失败的情况的系统。快速故障系统通常旨在停止正常操作,而不是尝试继续可能有缺陷的过程。此类设计通常在操作中的多个点检查系统状态,因此可以及早发现任何故障。快速故障模块的职责是检测错误,然后让系统的下一个最高级别处理它们。
断言仅用于调试目的,不应发生其触发条件(不应出现空指针等)
例外情况是可能总是发生的特殊系统事件:FileNotFound、ConnectionToServerLost 等。
断言和异常处理都可以保证程序的正确性并避免逻辑错误,
但是断言可以根据程序员的意愿启用和禁用,
在编译器中,如果您使用“java -ea JavaFileName”或“java -enableasserations JavaFileName”,您可以使用断言进行编译
如果程序员不需要它,“java -da JavaFileName”或“java -disableasserations JavaFileName”他们可以禁用断言。
此设施不在异常处理中
断言用于调试运行时需要检查的假设,仅通过启用断言功能,而异常是在程序执行期间检查要检查的特定条件,以防止程序终止。
对您期望发生的情况使用异常,对不应该发生的情况使用用户断言。
通常,在实际项目中,您希望异常涵盖不应该发生的情况并防止断言变为真。毕竟,您希望程序运行并且永远不会进入那种无效状态,因此您应该使用异常和其他技术来确保程序永远不会进入这种状态。然而,有时,有多种方式可以使断言变为真,而您当时只能预测其中的几种方式。因此,通过断言拥有这些“硬约束”可以确保如果将来程序处于您没有预料到的无效状态并且它不应该发生,它将停止并且您将不得不调查这是如何发生的。
换句话说,异常检查无效输入数据,而断言充当检测代码中错误的机制。