Java断言关键字做了什么,何时应该使用它?

本文翻译自:What does the Java assert keyword do, and when should it be used?

有哪些实际例子可以理解断言的关键作用?


#1楼

参考:https://stackoom.com/question/BZXU/Java断言关键字做了什么-何时应该使用它


#2楼

Assertion are basically used to debug the application or it is used in replacement of exception handling for some application to check the validity of an application. 断言主要用于调试应用程序,或者用于替换某些应用程序的异常处理以检查应用程序的有效性。

Assertion works at run time. 断言在运行时起作用。 A simple example, that can explain the whole concept very simply, is herein - What does the assert keyword do in Java? 这里可以非常简单地解释整个概念的一个简单例子 - assert关键字在Java中的作用是什么? (WikiAnswers). (WikiAnswers)。


#3楼

assert is a keyword. assert是一个关键字。 It was introduced in JDK 1.4. 它是在JDK 1.4中引入的。 The are two types of assert s 有两种类型的assert

  1. Very simple assert statements 非常简单的assert语句
  2. Simple assert statements. 简单assert语句。

By default all assert statements will not be executed. 默认情况下,不会执行所有assert语句。 If an assert statement receives false, then it will automatically raise an assertion error. 如果assert语句收到false,那么它将自动引发一个断言错误。


#4楼

Let's assume that you are supposed to write a program to control a nuclear power-plant. 让我们假设您应该编写一个程序来控制核电站。 It is pretty obvious that even the most minor mistake could have catastrophic results, therefore your code has to be bug-free (assuming that the JVM is bug-free for the sake of the argument). 很明显,即使是最小的错误也可能带来灾难性的结果,因此您的代码必须没有错误(假设JVM在参数方面没有错误)。

Java is not a verifiable language, which means: you cannot calculate that the result of your operation will be perfect. Java不是一种可验证的语言,这意味着:您无法计算出您的操作结果是否完美。 The main reason for this are pointers: they can point anywhere or nowhere, therefore they cannot be calculated to be of this exact value, at least not within a reasonable span of code. 这个的主要原因是指针:它们可以指向任何地方或任何地方,因此它们不能被计算为具有这个精确值,至少不在合理的代码范围内。 Given this problem, there is no way to prove that your code is correct at a whole. 鉴于此问题,无法证明您的代码在整体上是正确的。 But what you can do is to prove that you at least find every bug when it happens. 但你能做的就是证明你至少在发生错误时找到它们。

This idea is based on the Design-by-Contract (DbC) paradigm: you first define (with mathematical precision) what your method is supposed to do, and then verify this by testing it during actual execution. 这个想法基于契约式设计 (DbC)范例:您首先定义(以数学精度)您的方法应该做什么,然后通过在实际执行期间测试它来验证这一点。 Example: 例:

// Calculates the sum of a (int) + b (int) and returns the result (int).
int sum(int a, int b) {
  return a + b;
}

While this is pretty obvious to work fine, most programmers will not see the hidden bug inside this one (hint: the Ariane V crashed because of a similar bug). 虽然这很明显可以正常工作,但大多数程序员都不会在这个内部看到隐藏的错误(提示:由于类似的错误,Ariane V崩溃了)。 Now DbC defines that you must always check the input and output of a function to verify that it worked correctly. 现在,DbC定义您必须始终检查函数的输入和输出以验证它是否正常工作。 Java can do this through assertions: Java可以通过断言来做到这一点:

// Calculates the sum of a (int) + b (int) and returns the result (int).
int sum(int a, int b) {
    assert (Integer.MAX_VALUE - a >= b) : "Value of " + a + " + " + b + " is too large to add.";
  final int result = a + b;
    assert (result - a == b) : "Sum of " + a + " + " + b + " returned wrong sum " + result;
  return result;
}

Should this function now ever fail, you will notice it. 如果此功能现在失败,您会注意到它。 You will know that there is a problem in your code, you know where it is and you know what caused it (similar to Exceptions). 您将知道代码中存在问题,您知道它在哪里,并且您知道是什么导致它(类似于异常)。 And what is even more important: you stop executing right when it happens to prevent any further code to work with wrong values and potentially cause damage to whatever it controls. 更重要的是:当它碰巧阻止任何进一步的代码使用错误的值并且可能对其控制的任何内容造成损害时,你就会停止执行。

Java Exceptions are a similar concept, but they fail to verify everything. Java Exceptions是一个类似的概念,但它们无法验证所有内容。 If you want even more checks (at the cost of execution speed) you need to use assertions. 如果您想要更多检查(以执行速度为代价),则需要使用断言。 Doing so will bloat your code, but you can in the end deliver a product at a surprisingly short development time (the earlier you fix a bug, the lower the cost). 这样做会使您的代码膨胀,但最终您可以在极短的开发时间内交付产品(修复错误越早,成本就越低)。 And in addition: if there is any bug inside your code, you will detect it. 此外:如果代码中有任何错误,您将检测到它。 There is no way of a bug slipping-through and cause issues later. 没有办法让bug滑倒并在以后导致问题。

This still is not a guarantee for bug-free code, but it is much closer to that, than usual programs. 这仍然不是无错误代码的保证,但它比通常的程序更接近这一点。


#5楼

Here's the most common use case. 这是最常见的用例。 Suppose you're switching on an enum value: 假设您正在启用枚举值:

switch (fruit) {
  case apple:
    // do something
    break;
  case pear:
    // do something
    break;
  case banana:
    // do something
    break;
}

As long as you handle every case, you're fine. 只要你处理每一个案子,你都没事。 But someday, somebody will add fig to your enum and forget to add it to your switch statement. 但总有一天,有人会在你的枚举中添加fig并忘记将它添加到你的switch语句中。 This produces a bug that may get tricky to catch, because the effects won't be felt until after you've left the switch statement. 这会产生一个可能很难捕获的错误,因为在你离开switch语句之后才会感觉到效果。 But if you write your switch like this, you can catch it immediately: 但是如果你像这样编写你的开关,你可以立即抓住它:

switch (fruit) {
  case apple:
    // do something
    break;
  case pear:
    // do something
    break;
  case banana:
    // do something
    break;
  default:
    assert false : "Missing enum value: " + fruit;
}

#6楼

Assertions are a development-phase tool to catch bugs in your code. 断言是一种用于捕获代码中的错误的开发阶段工具。 They're designed to be easily removed, so they won't exist in production code. 它们的设计易于删除,因此它们不会存在于生产代码中。 So assertions are not part of the "solution" that you deliver to the customer. 因此,断言不是您向客户提供的“解决方案”的一部分。 They're internal checks to make sure that the assumptions you're making are correct. 它们是内部检查,以确保您所做的假设是正确的。 The most common example is to test for null. 最常见的例子是测试null。 Many methods are written like this: 许多方法都是这样写的:

void doSomething(Widget widget) {
  if (widget != null) {
    widget.someMethod(); // ...
    ... // do more stuff with this widget
  }
}

Very often in a method like this, the widget should simply never be null. 通常在这样的方法中,小部件应该永远不会为空。 So if it's null, there's a bug in your code somewhere that you need to track down. 因此,如果它为null,那么您的代码中存在一个需要跟踪的错误。 But the code above will never tell you this. 但上面的代码永远不会告诉你这一点。 So in a well-intentioned effort to write "safe" code, you're also hiding a bug. 因此,为了编写“安全”代码的善意努力,您也隐藏了一个错误。 It's much better to write code like this: 编写这样的代码要好得多:

/**
 * @param Widget widget Should never be null
 */
void doSomething(Widget widget) {
  assert widget != null;
  widget.someMethod(); // ...
    ... // do more stuff with this widget
}

This way, you will be sure to catch this bug early. 这样,您一定会尽早发现这个错误。 (It's also useful to specify in the contract that this parameter should never be null.) Be sure to turn assertions on when you test your code during development. (在合同中指定此参数永远不应为null也很有用。)确保在开发期间测试代码时打开断言。 (And persuading your colleagues to do this, too is often difficult, which I find very annoying.) (说服你的同事这样做也很困难,我觉得很烦人。)

Now, some of your colleagues will object to this code, arguing that you should still put in the null check to prevent an exception in production. 现在,你的一些同事会反对这段代码,认为你仍然应该进行空检查以防止生产中的异常。 In that case, the assertion is still useful. 在这种情况下,断言仍然有用。 You can write it like this: 你可以像这样写:

void doSomething(Widget widget) {
  assert widget != null;
  if (widget != null) {
    widget.someMethod(); // ...
    ... // do more stuff with this widget
  }
}

This way, your colleagues will be happy that the null check is there for production code, but during development, you're no longer hiding the bug when widget is null. 这样,您的同事会很高兴对生产代码进行空检查,但在开发期间,当窗口小部件为空时,您不再隐藏该错误。

Here's a real-world example: I once wrote a method that compared two arbitrary values for equality, where either value could be null: 这是一个真实的例子:我曾经写过一个方法,比较两个任意值的相等性,其中任何一个值都可以为null:

/**
 * Compare two values using equals(), after checking for null.
 * @param thisValue (may be null)
 * @param otherValue (may be null)
 * @return True if they are both null or if equals() returns true
 */
public static boolean compare(final Object thisValue, final Object otherValue) {
  boolean result;
  if (thisValue == null) {
    result = otherValue == null;
  } else {
    result = thisValue.equals(otherValue);
  }
  return result;
}

This code delegates the work of the equals() method in the case where thisValue is not null. 在thisValue不为null的情况下,此代码委托equals()方法的工作。 But it assumes the equals() method correctly fulfills the contract of equals() by properly handling a null parameter. 但它假设equals()方法通过正确处理null参数正确地履行了equals()的约定。

A colleague objected to my code, telling me that many of our classes have buggy equals() methods that don't test for null, so I should put that check into this method. 一位同事反对我的代码,告诉我我们的许多类都有错误的equals()方法,不测试null,所以我应该把检查放到这个方法中。 It's debatable if this is wise, or if we should force the error, so we can spot it and fix it, but I deferred to my colleague and put in a null check, which I've marked with a comment: 如果这是明智的,或者我们应该强制错误,那么我们可以发现它并修复它,这是有争议的,但我推迟到我的同事并进行空检查,我已经标记了注释:

public static boolean compare(final Object thisValue, final Object otherValue) {
  boolean result;
  if (thisValue == null) {
    result = otherValue == null;
  } else {
    result = otherValue != null && thisValue.equals(otherValue); // questionable null check
  }
  return result;
}

The additional check here, other != null , is only necessary if the equals() method fails to check for null as required by its contract. 只有当equals()方法无法按照其合同的要求检查other != null ,才需要在此处进行附加检查, other != null

Rather than engage in a fruitless debate with my colleague about the wisdom of letting the buggy code stay in our code base, I simply put two assertions in the code. 我没有与我的同事就让错误代码留在我们的代码库中的智慧进行毫无结果的辩论,而是简单地在代码中加入了两个断言。 These assertions will let me know, during the development phase, if one of our classes fails to implement equals() properly, so I can fix it: 这些断言让我知道,在开发阶段,如果我们的一个类无法正确实现equals() ,那么我可以解决它:

public static boolean compare(final Object thisValue, final Object otherValue) {
  boolean result;
  if (thisValue == null) {
    result = otherValue == null;
    assert otherValue == null || otherValue.equals(null) == false;
  } else {
    result = otherValue != null && thisValue.equals(otherValue);
    assert thisValue.equals(null) == false;
  }
  return result;
}

The important points to keep in mind are these: 要记住的重点是:

  1. Assertions are development-phase tools only. 断言只是开发阶段工具。

  2. The point of an assertion is to let you know if there's a bug, not just in your code, but in your code base . 断言的目的是让你知道是否存在错误,不只是在你的代码中,而是在你的代码库中 (The assertions here will actually flag bugs in other classes.) (这里的断言实际上会标记其他类中的错误。)

  3. Even if my colleague was confident that our classes were properly written, the assertions here would still be useful. 即使我的同事确信我们的课程写得很好,这里的断言仍然有用。 New classes will be added that might fail to test for null, and this method can flag those bugs for us. 将添加可能无法测试null的新类,此方法可以为我们标记这些错误。

  4. In development, you should always turn assertions on, even if the code you've written doesn't use assertions. 在开发过程中,即使您编写的代码不使用断言,也应始终打开断言。 My IDE is set to always do this by default for any new executable. 我的IDE设置为默认为任何新的可执行文件执行此操作。

  5. The assertions don't change the behavior of the code in production, so my colleague is happy that the null check is there, and that this method will execute properly even if the equals() method is buggy. 断言不会改变生产中代码的行为,所以我的同事很高兴有空检查,并且即使equals()方法有问题,这个方法也会正确执行。 I'm happy because I will catch any buggy equals() method in development. 我很高兴,因为我会在开发中捕获任何错误的equals()方法。

Also, you should test your assertion policy by putting in a temporary assertion that will fail, so you can be certain that you are notified, either through the log file or a stack trace in the output stream. 此外,您应该通过放入一个失败的临时断言来测试断言策略,这样您就可以确定通过日志文件或输出流中的堆栈跟踪得到通知。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值