Java 中的断言是什么以及何时应该使用它们?

发布于 2024-08-31 03:14:34 字数 77 浏览 2 评论 0原文

有哪些现实生活中的示例可以帮助您了解 Java assert 关键字的关键作用?

What are some real life examples to understand the key role of the Java assert keyword?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(21

猥︴琐丶欲为 2024-09-07 03:14:34

断言(通过assert 关键字)是在 Java 1.4 中添加的。它们用于验证代码中不变量的正确性。它们永远不应该在生产代码中触发,并且表明代码路径存在错误或误用。它们可以在运行时通过 java 命令上的 -ea 选项激活,但默认情况下不会打开。

一个例子:

public Foo acquireFoo(int id) {
    Foo result = (id > 50) ? fooService.read(id) : new Foo(id);

    assert result != null;

    return result;
}

Assertions (by way of the assert keyword) were added in Java 1.4. They are used to verify the correctness of an invariant in the code. They should never be triggered in production code, and are indicative of a bug or misuse of a code path. They can be activated at run-time by way of the -ea option on the java command, but are not turned on by default.

An example:

public Foo acquireFoo(int id) {
    Foo result = (id > 50) ? fooService.read(id) : new Foo(id);

    assert result != null;

    return result;
}
笑脸一如从前 2024-09-07 03:14:34

假设您要编写一个程序来控制核电站。很明显,即使是最微小的错误也可能会产生灾难性的结果,因此您的代码必须没有错误(为了论证,假设 JVM 没有错误)。

Java 不是可验证的语言,这意味着:你无法计算出你的操作结果会是完美的。其主要原因是指针:它们可以指向任何地方或无处,因此无法计算出它们的精确值,至少不能在合理的代码范围内。鉴于这个问题,没有办法证明你的代码整体上是正确的。但你能做的是证明你至少能在错误发生时发现它。

这个想法基于 Design-by-Contract (DbC) 范例:您首先定义(使用数学精度)你的方法应该做什么,然后通过在实际执行期间测试它来验证这一点。示例:

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

虽然这很明显可以正常工作,但大多数程序员不会看到其中隐藏的错误(提示:Ariane V 由于类似的错误而崩溃)。现在,DbC 定义您必须始终检查函数的输入和输出以验证其是否正常工作。 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;
}

如果这个函数现在失败,您会注意到它。你会知道你的代码中有问题,你知道它在哪里,你知道是什么导致了它(类似于异常)。更重要的是:当它发生时,你就停止执行,以防止任何进一步的代码使用错误的值并可能对其控制的任何内容造成损害。

Java 异常是一个类似的概念,但它们无法验证所有内容。如果您想要更多检查(以执行速度为代价),您需要使用断言。这样做会使您的代码变得臃肿,但您最终可以在非常短的开发时间内交付产品(越早修复错误,成本越低)。此外:如果您的代码中存在任何错误,您将检测到它。错误不可能溜走并在以后引起问题。

这仍然不能保证代码没有错误,但它比通常的程序更接近这一点。

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).

Java is not a verifiable language, which means: you cannot calculate that the result of your operation will be perfect. 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. 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). Now DbC defines that you must always check the input and output of a function to verify that it worked correctly. Java can do this through assertions:

// 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. 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.

This still is not a guarantee for bug-free code, but it is much closer to that, than usual programs.

深海夜未眠 2024-09-07 03:14:34

断言是一种开发阶段的工具,用于捕获代码中的错误。它们被设计为易于删除,因此它们不会存在于生产代码中。因此,断言不是您提供给客户的“解决方案”的一部分。它们是内部检查,以确保您所做的假设是正确的。最常见的示例是测试 null。许多方法都是这样编写的:

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

通常在这样的方法中,小部件不应该为 null。因此,如果它为空,则说明您的代码中存在需要追踪的错误。但上面的代码永远不会告诉你这一点。因此,在编写“安全”代码的善意努力中,您也隐藏了一个错误。最好像这样编写代码:

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

这样,您就一定能及早发现这个错误。 (在合同中指定此参数永远不应该为空也很有用。)在开发过程中测试代码时,请务必打开断言。 (说服你的同事这样做通常也很困难,我觉得这很烦人。)

现在,你的一些同事会反对这段代码,认为你仍然应该进行空检查以防止生产中出现异常。在这种情况下,这个断言仍然有用。你可以这样写:

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

这样,你的同事会很高兴生产代码有空检查,但在开发过程中,当小部件为空时,你不再隐藏错误。

这是一个真实世界的示例:我曾经编写过一个方法,用于比较两个任意值是否相等,其中任一值都可以为 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;
}

在 thisValue 的情况下,此代码委托 equals() 方法的工作不为空。但它假设 equals() 方法通过正确处理 null 参数来正确履行 equals() 的约定。

一位同事反对我的代码,告诉我我们的许多类都有错误的 equals() 方法,这些方法不测试 null,所以我应该将该检查放入此方法中。这是否明智,或者我们是否应该强制错误,以便我们可以发现它并修复它,这是有争议的,但我听从了我的同事的意见,并进行了空检查,我已用注释标记了它:

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;
}

此处的附加检查, other != null 仅当 equals() 方法未能按照其约定的要求检查 null 时才需要。

我没有与我的同事就让有缺陷的代码保留在我们的代码库中是否明智进行徒劳的辩论,而是简单地在代码中添加了两个断言。这些断言会让我知道,在开发阶段,如果我们的某个类未能正确实现 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;
}

需要记住的要点是:

  1. 断言只是开发阶段的工具。

  2. 断言的目的是让您知道是否存在错误,不仅在您的代码中,而且在您的代码库中。 (这里的断言实际上会标记其他类中的错误。)

  3. 即使我的同事确信我们的类已正确编写,这里的断言仍然有用。将添加可能无法测试 null 的新类,并且此方法可以为我们标记这些错误。

  4. 在开发中,您应该始终打开断言,即使您编写的代码不使用断言。我的 IDE 设置为默认情况下始终对任何新的可执行文件执行此操作。

  5. 断言不会改变生产中代码的行为,因此我的同事很高兴有 null 检查,并且即使 equals() 方法也能正确执行此方法是越野车。我很高兴,因为我会在开发中发现任何有问题的 equals() 方法。

另外,您应该通过放入一个将失败的临时断言来测试您的断言策略,这样您就可以确定您收到了通知,无论是通过日志文件还是输出流中的堆栈跟踪。

(我应该补充一点,我的同事关于我们的代码有错误的说法是错误的。这些断言都没有被触发。正如我所期望的,我们的代码很好。)

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. 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. 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. (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:

/**
 * 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. But it assumes the equals() method correctly fulfills the contract of equals() by properly handling a null parameter.

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. 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.

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:

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.

  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.

  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. I'm happy because I will catch any buggy equals() method in development.

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.

(I should add that my colleague was wrong about our code being buggy. None of these assertions were ever triggered. Our code was fine, as I expected.)

辞旧 2024-09-07 03:14:34

什么时候应该使用断言?

有很多很好的答案解释了 assert 关键字的作用,但很少回答真正的问题,“在现实生活中何时应该使用 assert 关键字?”< /em> 答案:

几乎从不

断言作为一个概念,非常棒。好的代码有很多 if (...) throw ... 语句(以及它们的相关语句,例如 Objects.requireNonNullMath.addExact) 。然而,某些设计决策极大地限制了 assert 关键字 本身的实用性。

assert 关键字背后的驱动思想是过早优化,主要功能是能够轻松关闭所有检查。事实上,默认情况下,assert 检查是关闭的。

但是,在生产中继续进行不变检查至关重要。这是因为完美的测试覆盖是不可能的,并且所有生产代码都会有错误,断言应该有助于诊断和缓解。

因此,应该优先使用 if (...) throw ...,就像检查公共方法的参数值和抛出 IllegalArgumentException 所需要的那样。

有时,人们可能会想编写一个不变的检查,该检查确实需要花费非常长的时间来处理(并且被调用的频率足以使其发挥作用)。然而,这样的检查会减慢测试速度,这也是不希望的。这种耗时的检查通常被编写为单元测试。尽管如此,出于这个原因,有时使用 assert 可能是有意义的。

不要仅仅因为 assertif (...) throw ... 更干净、更漂亮(我很痛苦地这么说,因为我喜欢干净和漂亮)漂亮的)。如果您无法控制自己,并且可以控制应用程序的启动方式,那么请随意使用 assert,但始终在生产中启用断言。 不可否认,这就是我所倾向于的去做。我正在推动一个 lombok 注释,它将导致 assert 的行为更像 if (...) throw ...在这里投票。

(咆哮:JVM 开发人员是一群糟糕的、过早优化的人)这就是为什么您会在 Java 插件和 JVM 中听到如此多的安全问题。他们拒绝在生产代码中包含基本的检查和断言,而我们将继续为此付出代价。)

When should Assert be used?

A lot of good answers explaining what the assert keyword does, but few answering the real question, "when should the assert keyword be used in real life?" The answer:

Almost never

Assertions, as a concept, are wonderful. Good code has lots of if (...) throw ... statements (and their relatives like Objects.requireNonNull and Math.addExact). However, certain design decisions have greatly limited the utility of the assert keyword itself.

The driving idea behind the assert keyword is premature optimization, and the main feature is being able to easily turn off all checks. In fact, the assert checks are turned off by default.

However, it is critically important that invariant checks continue to be done in production. This is because perfect test coverage is impossible, and all production code will have bugs which assertions should help to diagnose and mitigate.

Therefore, the use of if (...) throw ... should be preferred, just as it is required for checking parameter values of public methods and for throwing IllegalArgumentException.

Occasionally, one might be tempted to write an invariant check that does take an undesirably long time to process (and is called often enough for it to matter). However, such checks will slow down testing which is also undesirable. Such time-consuming checks are usually written as unit tests. Nevertheless, it may sometimes make sense to use assert for this reason.

Do not use assert simply because it is cleaner and prettier than if (...) throw ... (and I say that with great pain, because I like clean and pretty). If you just cannot help yourself, and can control how your application is launched, then feel free to use assert but always enable assertions in production. Admittedly, this is what I tend to do. I am pushing for a lombok annotation that will cause assert to act more like if (...) throw .... Vote for it here.

(Rant: the JVM devs were a bunch of awful, prematurely optimizing coders. That is why you hear about so many security issues in the Java plugin and JVM. They refused to include basic checks and assertions in production code, and we are continuing to pay the price.)

巷子口的你 2024-09-07 03:14:34

这是最常见的用例。假设您要打开一个枚举值:

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

只要处理每种情况,就可以了。但有一天,有人会将 Fig 添加到您的枚举中,却忘记将其添加到您的 switch 语句中。这会产生一个可能很难捕捉的错误,因为只有在离开 switch 语句之后才会感受到效果。但是如果你这样写你的开关,你可以立即捕获它:

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

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. 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. 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;
}
笑着哭最痛 2024-09-07 03:14:34

Java中的assert关键字有什么作用?

让我们看看编译后的字节码。

我们将得出结论:

public class Assert {
    public static void main(String[] args) {
        assert System.currentTimeMillis() == 0L;
    }
}

生成几乎完全相同的字节码:

public class Assert {
    static final boolean $assertionsDisabled =
        !Assert.class.desiredAssertionStatus();
    public static void main(String[] args) {
        if (!$assertionsDisabled) {
            if (System.currentTimeMillis() != 0L) {
                throw new AssertionError();
            }
        }
    }
}

其中 Assert.class.desiredAssertionStatus()true 当在命令行上传递 -ea 时,否则为 false。

我们使用 System.currentTimeMillis() 来确保它不会被优化(assert true; 确实如此)。

生成合成字段后,Java 只需在加载时调用 Assert.class.desiredAssertionStatus() 一次,然后将结果缓存在那里。 参阅:“静态合成”是什么意思?

另请 可以通过以下方式验证:

javac Assert.java
javap -c -constants -private -verbose Assert.class

使用 Oracle JDK 1.8.0_45,生成了一个合成静态字段(另请参阅:“静态合成”是什么意思?):

static final boolean $assertionsDisabled;
  descriptor: Z
  flags: ACC_STATIC, ACC_FINAL, ACC_SYNTHETIC

与静态初始化器一起使用:

 0: ldc           #6                  // class Assert
 2: invokevirtual #7                  // Method java/lang Class.desiredAssertionStatus:()Z
 5: ifne          12
 8: iconst_1
 9: goto          13
12: iconst_0
13: putstatic     #2                  // Field $assertionsDisabled:Z
16: return

主要方法是:

 0: getstatic     #2                  // Field $assertionsDisabled:Z
 3: ifne          22
 6: invokestatic  #3                  // Method java/lang/System.currentTimeMillis:()J
 9: lconst_0
10: lcmp
11: ifeq          22
14: new           #4                  // class java/lang/AssertionError
17: dup
18: invokespecial #5                  // Method java/lang/AssertionError."<init>":()V
21: athrow
22: return

我们得出的结论是:

  • assert:这是一个 Java 语言概念
  • assert 可以通过系统属性 -Pcom.me.assert=true 很好地模拟来替换 -ea在命令行上,以及抛出新的 AssertionError()

What does the assert keyword in Java do?

Let's look at the compiled bytecode.

We will conclude that:

public class Assert {
    public static void main(String[] args) {
        assert System.currentTimeMillis() == 0L;
    }
}

generates almost the exact same bytecode as:

public class Assert {
    static final boolean $assertionsDisabled =
        !Assert.class.desiredAssertionStatus();
    public static void main(String[] args) {
        if (!$assertionsDisabled) {
            if (System.currentTimeMillis() != 0L) {
                throw new AssertionError();
            }
        }
    }
}

where Assert.class.desiredAssertionStatus() is true when -ea is passed on the command line, and false otherwise.

We use System.currentTimeMillis() to ensure that it won't get optimized away (assert true; did).

The synthetic field is generated so that Java only needs to call Assert.class.desiredAssertionStatus() once at load time, and it then caches the result there. See also: What is the meaning of "static synthetic"?

We can verify that with:

javac Assert.java
javap -c -constants -private -verbose Assert.class

With Oracle JDK 1.8.0_45, a synthetic static field was generated (see also: What is the meaning of "static synthetic"?):

static final boolean $assertionsDisabled;
  descriptor: Z
  flags: ACC_STATIC, ACC_FINAL, ACC_SYNTHETIC

together with a static initializer:

 0: ldc           #6                  // class Assert
 2: invokevirtual #7                  // Method java/lang Class.desiredAssertionStatus:()Z
 5: ifne          12
 8: iconst_1
 9: goto          13
12: iconst_0
13: putstatic     #2                  // Field $assertionsDisabled:Z
16: return

and the main method is:

 0: getstatic     #2                  // Field $assertionsDisabled:Z
 3: ifne          22
 6: invokestatic  #3                  // Method java/lang/System.currentTimeMillis:()J
 9: lconst_0
10: lcmp
11: ifeq          22
14: new           #4                  // class java/lang/AssertionError
17: dup
18: invokespecial #5                  // Method java/lang/AssertionError."<init>":()V
21: athrow
22: return

We conclude that:

  • there is no bytecode level support for assert: it is a Java language concept
  • assert could be emulated pretty well with system properties -Pcom.me.assert=true to replace -ea on the command line, and a throw new AssertionError().
骑趴 2024-09-07 03:14:34

断言用于检查后置条件和“永远不会失败”的前置条件。正确的代码决不应该使断言失败;当它们触发时,它们应该指示一个错误(希望在靠近问题实际位置的地方)。

断言的一个示例可能是检查是否以正确的顺序调用特定的一组方法(例如,在迭代器)。

Assertions are used to check post-conditions and "should never fail" pre-conditions. Correct code should never fail an assertion; when they trigger, they should indicate a bug (hopefully at a place that is close to where the actual locus of the problem is).

An example of an assertion might be to check that a particular group of methods is called in the right order (e.g., that hasNext() is called before next() in an Iterator).

枉心 2024-09-07 03:14:34

断言允许检测代码中的缺陷。您可以打开断言以进行测试和调试,同时在程序投入生产时将其关闭。

当你知道某件事是真的时,为什么还要断言它呢?只有当一切正常时才是如此。如果程序有缺陷,它实际上可能不是真的。在此过程中尽早检测到这一点可以让您知道出现了问题。

assert 语句包含此语句以及可选的String 消息。

断言语句的语法有两种形式:

assert boolean_expression;
assert boolean_expression: error_message;

以下是一些基本规则,用于控制应在何处使用断言以及不应在何处使用断言。断言应该用于:

  1. 验证私有方法的输入参数。对于公共方法public 方法在传递错误参数时应该抛出常规异常。

  2. 程序中的任何地方都可以确保几乎肯定是真实的事实的有效性。

例如,如果您确定它只会是 1 或 2,则可以使用如下断言:

...
if (i == 1)    {
    ...
}
else if (i == 2)    {
    ...
} else {
    assert false : "cannot happen. i is " + i;
}
...
  1. 在任何方法末尾验证后置条件。这意味着,在执行业务逻辑之后,您可以使用断言来确保变量或结果的内部状态与您的期望一致。例如,打开套接字或文件的方法可以在末尾使用断言来确保套接字或文件确实被打开。

断言不应该用于:

  1. 验证公共方法的输入参数。由于断言可能并不总是被执行,因此应该使用常规异常机制。

  2. 验证对用户输入内容的约束。与上面相同。

  3. 不应该因为副作用而使用。

例如,这不是正确的用法,因为这里使用断言是因为其调用 doSomething() 方法的副作用。

public boolean doSomething() {
...    
}
public void someMethod() {       
assert doSomething(); 
}

唯一可以证明这一点的情况是当您试图查明代码中是否启用了断言时:   

boolean enabled = false;    
assert enabled = true;    
if (enabled) {
    System.out.println("Assertions are enabled");
} else {
    System.out.println("Assertions are disabled");
}

An assertion allows for detecting defects in the code. You can turn on assertions for testing and debugging while leaving them off when your program is in production.

Why assert something when you know it is true? It is only true when everything is working properly. If the program has a defect, it might not actually be true. Detecting this earlier in the process lets you know something is wrong.

An assert statement contains this statement along with an optional String message.

The syntax for an assert statement has two forms:

assert boolean_expression;
assert boolean_expression: error_message;

Here are some basic rules which govern where assertions should be used and where they should not be used. Assertions should be used for:

  1. Validating input parameters of a private method. NOT for public methods. public methods should throw regular exceptions when passed bad parameters.

  2. Anywhere in the program to ensure the validity of a fact which is almost certainly true.

For example, if you are sure that it will only be either 1 or 2, you can use an assertion like this:

...
if (i == 1)    {
    ...
}
else if (i == 2)    {
    ...
} else {
    assert false : "cannot happen. i is " + i;
}
...
  1. Validating post conditions at the end of any method. This means, after executing the business logic, you can use assertions to ensure that the internal state of your variables or results is consistent with what you expect. For example, a method that opens a socket or a file can use an assertion at the end to ensure that the socket or the file is indeed opened.

Assertions should not be used for:

  1. Validating input parameters of a public method. Since assertions may not always be executed, the regular exception mechanism should be used.

  2. Validating constraints on something that is input by the user. Same as above.

  3. Should not be used for side effects.

For example this is not a proper use because here the assertion is used for its side effect of calling of the doSomething() method.

public boolean doSomething() {
...    
}
public void someMethod() {       
assert doSomething(); 
}

The only case where this could be justified is when you are trying to find out whether or not assertions are enabled in your code:   

boolean enabled = false;    
assert enabled = true;    
if (enabled) {
    System.out.println("Assertions are enabled");
} else {
    System.out.println("Assertions are disabled");
}
淡墨 2024-09-07 03:14:34

来自 Stack 类的真实示例(来自 Java 文章中的断言)

public int pop() {
   // precondition
   assert !isEmpty() : "Stack is empty";
   return stack[--num];
}

A real world example, from a Stack-class (from Assertion in Java Articles)

public int pop() {
   // precondition
   assert !isEmpty() : "Stack is empty";
   return stack[--num];
}
以往的大感动 2024-09-07 03:14:34

开发时断言非常很有用。当您的代码正常工作而不可能发生某些事情时,您可以使用它。它很容易使用,并且可以永远保留在代码中,因为它在现实生活中会被关闭。

如果这种情况有可能在现实生活中发生,那么你必须处理它。

我喜欢它,但不知道如何在 Eclipse/Android/ADT 中打开它。即使在调试时也似乎关闭了。 (对此有一个线程,但它指的是“Java vm”,它不会出现在 ADT 运行配置中)。

Assert is very useful when developing. You use it when something just cannot happen if your code is working correctly. It's easy to use, and can stay in the code for ever, because it will be turned off in real life.

If there is any chance that the condition can occur in real life, then you must handle it.

I love it, but don't know how to turn it on in Eclipse/Android/ADT . It seems to be off even when debugging. (There is a thread on this, but it refers to the 'Java vm', which does not appear in the ADT Run Configuration).

夜无邪 2024-09-07 03:14:34

断言是可能被关闭的检查。它们很少被使用。为什么?

  • 它们不得用于检查公共方法参数,因为您无法控制它们。
  • 它们不应该用于像 result != null 这样的简单检查,因为这样的检查非常快,而且几乎没有什么可以保存的。

那么,还剩下什么呢? 昂贵检查是否确实预期为真。一个很好的例子是 RB 树等数据结构的不变量。实际上,在JDK8的ConcurrentHashMap中,对于TreeNodes有几个这样有意义的断言。

  • 您确实不想在生产中打开它们,因为它们很容易主导运行时间。
  • 您可能想在测试期间打开或关闭它们。
  • 您肯定想在编写代码时打开它们。

有时,检查并不是很贵,但同时,您非常确定它会通过。在我的代码中,例如,

assert Sets.newHashSet(userIds).size() == userIds.size();

我非常确定我刚刚创建的列表具有独特的元素,但我想记录并仔细检查它。

Assertions are checks which may get switched off. They're rarely used. Why?

  • They must not be used for checking public method arguments as you have no control over them.
  • They should not be used for simple checks like result != null as such checks are very fast and there's hardly anything to save.

So, what's left? Expensive checks for conditions really expected to be true. A good example would be the invariants of a data structure like RB-tree. Actually, in ConcurrentHashMap of JDK8, there are a few such meaningful asserts for the TreeNodes.

  • You really don't want to switch them on in production as they could easily dominate the run time.
  • You may want to switch them on or off during tests.
  • You definitely want to switch them on when working on the code.

Sometimes, the check is not really expensive, but at the same time, you're pretty sure, it'll pass. In my code, there's e.g.,

assert Sets.newHashSet(userIds).size() == userIds.size();

where I'm pretty sure that the list I just created has unique elements, but I wanted to document and double check it.

原谅过去的我 2024-09-07 03:14:34

除了这里提供的所有精彩答案之外,官方 Java SE 7 编程指南还有一个关于使用 assert 的非常简洁的手册;有几个恰当的例子说明什么时候使用断言是一个好主意(而且更重要的是,是坏主意),以及它与抛出异常有何不同。

链接

In addition to all the great answers provided here, the official Java SE 7 programming guide has a pretty concise manual on using assert; with several spot-on examples of when it's a good (and, importantly, bad) idea to use assertions, and how it's different from throwing exceptions.

Link

独﹏钓一江月 2024-09-07 03:14:34

这是我在 Hibernate/SQL 项目的服务器中编写的断言。实体 bean 有两个有效的布尔属性,称为 isActive 和 isDefault。每个值可以为“Y”或“N”或 null(被视为“N”)。我们希望确保浏览器客户端仅限于这三个值。因此,在这两个属性的设置器中,我添加了以下断言:

assert new HashSet<String>(Arrays.asList("Y", "N", null)).contains(value) : value;

请注意以下内容。

  1. 此断言仅适用于开发阶段。如果客户发送了错误的值,我们将在投入生产之前尽早发现并修复它。断言适用于您可以及早发现的缺陷。

  2. 这个断言缓慢且低效。没关系。断言可以很慢。我们不在乎,因为它们只是开发工具。这不会减慢生产代码的速度,因为断言将被禁用。 (在这一点上存在一些分歧,我稍后会讨论。)这引出了我的下一个观点。

  3. 这个断言没有副作用。我可以针对不可修改的静态最终集测试我的值,但该集将保留在生产中,永远不会被使用。

  4. 此断言的存在是为了验证客户端的正确操作。因此,当我们到达生产环境时,我们将确保客户端运行正常,因此我们可以安全地关闭断言。

  5. 有人问:如果生产中不需要断言,为什么不在完成后将它们取出呢?因为当你开始开发下一个版本时,你仍然需要它们。

有些人认为你永远不应该使用断言,因为你永远无法确定所有错误都消失了,所以即使在生产中你也需要保留它们。因此,使用断言语句是没有意义的,因为断言的唯一优点是您可以将其关闭。因此,根据这种想法,您(几乎)永远不应该使用断言。我不同意。确实,如果测试属于生产环境,则不应使用断言。但此测试属于生产环境。这个用于捕获不太可能到达生产环境的错误,因此在完成后可以安全地关闭它。

顺便说一句,我可以这样写:

assert value == null || value.equals("Y") || value.equals("N") : value;

这对于只有三个值来说很好,但是如果可能值的数量变大,HashSet 版本就会变得更方便。我选择 HashSet 版本是为了表达我关于效率的观点。

Here's an assertion I wrote in a server for a Hibernate/SQL project. An entity bean had two effectively-boolean properties, called isActive and isDefault. Each could have a value of "Y" or "N" or null, which was treated as "N". We want to make sure the browser client is limited to these three values. So, in my setters for these two properties, I added this assertion:

assert new HashSet<String>(Arrays.asList("Y", "N", null)).contains(value) : value;

Notice the following.

  1. This assertion is for the development phase only. If the client sends a bad value, we will catch that early and fix it, long before we reach production. Assertions are for defects that you can catch early.

  2. This assertion is slow and inefficient. That's okay. Assertions are free to be slow. We don't care because they're development-only tools. This won't slow down the production code because assertions will be disabled. (There's some disagreement on this point, which I'll get to later.) This leads to my next point.

  3. This assertion has no side effects. I could have tested my value against an unmodifiable static final Set, but that set would have stayed around in production, where it would never get used.

  4. This assertion exists to verify the proper operation of the client. So by the time we reach production, we will be sure that the client is operating properly, so we can safely turn the assertion off.

  5. Some people ask this: If the assertion isn't needed in production, why not just take them out when you're done? Because you'll still need them when you start working on the next version.

Some people have argued that you should never use assertions, because you can never be sure that all the bugs are gone, so you need to keep them around even in production. And so there's no point in using the assert statement, since the only advantage to asserts is that you can turn them off. Hence, according to this thinking, you should (almost) never use asserts. I disagree. It's certainly true that if a test belongs in production, you should not use an assert. But this test does not belong in production. This one is for catching a bug that's not likely to ever reach production, so it may safely be turned off when you're done.

BTW, I could have written it like this:

assert value == null || value.equals("Y") || value.equals("N") : value;

This is fine for only three values, but if the number of possible values gets bigger, the HashSet version becomes more convenient. I chose the HashSet version to make my point about efficiency.

尾戒 2024-09-07 03:14:34

回顾一下(这对于许多语言都是如此,而不仅仅是 Java):

“assert”主要用作软件开发人员在调试过程中的调试辅助工具。断言消息不应出现。许多语言提供编译时选项,该选项将导致所有“断言”被忽略,以用于生成“生产”代码。

“异常”是处理各种错误情况的便捷方法,无论它们是否代表逻辑错误,因为,如果您遇到无法继续的错误情况,您可以简单地“将它们扔到空中,无论你身在何处,都期待其他人准备好“抓住”他们。控制权一步到位,直接从引发异常的代码直接转移到捕手的手套上。 (捕获器可以看到已发生的调用的完整回溯。)

此外,该子例程的调用者不必检查子例程是否成功:“如果我们'现在就在这里,它一定已经成功了,因为否则它就会抛出异常,我们现在就不会在这里了!”这个简单的策略使代码设计和调试变得非常非常容易。

异常方便地允许致命错误条件成为它们本来的样子:“规则的例外”。并且,对于它们由代码路径进行处理,该代码路径也是“规则的例外......“飞球!”

To recap (and this is true of many languages not just Java):

"assert" is primarily used as a debugging aid by software developers during the debugging process. Assert-messages should never appear. Many languages provide a compile-time option that will cause all "asserts" to be ignored, for use in generating "production" code.

"exceptions" are a handy way to handle all kinds of error conditions, whether or not they represent logic errors, because, if you run into an error-condition such that you cannot continue, you can simply "throw them up into the air," from wherever you are, expecting someone else out there to be ready to "catch" them. Control is transferred in one step, straight from the code that threw the exception, straight to the catcher's mitt. (And the catcher can see the complete backtrace of calls that had taken place.)

Furthermore, callers of that subroutine don't have to check to see if the subroutine succeeded: "if we're here now, it must have succeeded, because otherwise it would have thrown an exception and we wouldn't be here now!" This simple strategy makes code-design and debugging much, much easier.

Exceptions conveniently allow fatal-error conditions to be what they are: "exceptions to the rule." And, for them to be handled by a code-path that is also "an exception to the rule ... "fly ball!"

人海汹涌 2024-09-07 03:14:34

断言基本上用于调试应用程序,或者用于替换某些应用程序的异常处理以检查应用程序的有效性。

断言在运行时起作用。这里有一个简单的例子,可以非常简单地解释整个概念 - assert 关键字是什么在 Java 中做什么?(WikiAnswers)。

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? (WikiAnswers).

嗼ふ静 2024-09-07 03:14:34

默认情况下禁用断言。为了启用它们,我们必须使用 -ea 选项运行程序(粒度可以变化)。例如,java -ea AssertionsDemo

使用断言有两种格式:

  1. 简单:例如。 断言 1==2; // 这将引发断言错误
  2. 更好: assert 1==2: "不可能.. 1 不等于 2";
    这将引发一个 AssertionError 并显示给定的消息,因此更好。虽然实际语法是 assert expr1:expr2 其中 expr2 可以是任何返回值的表达式,但我更经常使用它只是为了打印消息。

Assertions are disabled by default. To enable them we must run the program with -ea options (granularity can be varied). For example, java -ea AssertionsDemo.

There are two formats for using assertions:

  1. Simple: eg. assert 1==2; // This will raise an AssertionError.
  2. Better: assert 1==2: "no way.. 1 is not equal to 2";
    This will raise an AssertionError with the message given displayed too and is thus better. Although the actual syntax is assert expr1:expr2 where expr2 can be any expression returning a value, I have used it more often just to print a message.
哥,最终变帅啦 2024-09-07 03:14:34

这是另一个例子。我编写了一个方法来查找两个排序数组中值的中位数。该方法假设数组已经排序。出于性能原因,它不应该首先对数组进行排序,甚至不应该检查以确保它们已排序。 (首先对数组进行排序会将 log(n) 方法转换为 n(log(n)) 方法。)但是,使用未排序的数据调用此方法是一个严重的错误,我们希望尽早发现这些错误,例如发展阶段。因此,我是这样处理这些看似相互冲突的目标的:

public static int medianOf(int[] a, int[] b) {
  assert assertionOnlyIsSorted(a);      // Assertion is order n
  assert assertionOnlyIsSorted(b);
  ... // rest of implementation goes here. Algorithm is order log(n)
}

public static boolean assertionOnlyIsSorted(int[] array) {
  for (int i=1; i<array.length; ++i) {
    if (array[i] < array[i-1]) {
      return false;
    }
  }
  return true;
}

这样,缓慢的测试仅在开发阶段执行,在开发阶段,速度不如捕获错误重要。您希望 medianOf() 方法具有 log(n) 性能,但“已排序”测试的顺序是 n。因此,我将其放在断言中,以将其使用限制在开发阶段,并且我给它起了一个名称,以明确它不适合生产。

这样我就可以两全其美。在开发过程中,我知道任何错误调用此方法的方法都会被捕获并修复。而且我知道这样做的缓慢测试不会影响生产中的性能。 (这也很好地说明了为什么要在生产中关闭断言,但在开发中打开它们。)

Here's another example. I wrote a method that finds the median of the values in two sorted arrays. The method assumes the arrays are already sorted. For performance reasons, it should NOT sort the arrays first, or even check to ensure they're sorted. (Sorting the arrays first would turn the log(n) method to an n(log(n)) method.) However, it's a serious bug to call this method with unsorted data, and we want those bugs to get caught early, in the development phase. So here's how I handled those seemingly conflicting goals:

public static int medianOf(int[] a, int[] b) {
  assert assertionOnlyIsSorted(a);      // Assertion is order n
  assert assertionOnlyIsSorted(b);
  ... // rest of implementation goes here. Algorithm is order log(n)
}

public static boolean assertionOnlyIsSorted(int[] array) {
  for (int i=1; i<array.length; ++i) {
    if (array[i] < array[i-1]) {
      return false;
    }
  }
  return true;
}

This way, the test, which is slow, is only performed during the development phase, where speed is less important than catching bugs. You want the medianOf() method to have log(n) performance, but the "is sorted" test is order n. So I put it inside an assertion, to limit its use to the development phase, and I give it a name that makes it clear it's not suitable for production.

This way I have the best of both worlds. In development, I know that any method that calls this incorrectly will get caught and fixed. And I know that the slow test to do so won't affect performance in production. (It's also a good illustration of why you want to leave assertions off in production, but turn them on in development.)

情愿 2024-09-07 03:14:34

上面有大量的热量(和有限的光线)。

这是我作为一个习惯使用安全关键系统的人的观点(这些系统如果出错,只会杀死几百人,而且你很幸运)。

断言(在所有语言中,不仅仅是 Java)是一种开发工具,用于帮助确保程序逻辑的正确性,永远不应在生产代码中启用。它们有两个目的:

  1. 文档。与调用函数时的状态相关的断言(“前提条件”)记录“如果你不处于这种状态,你就不敢调用这个函数。如果我没有把这个前提条件放入,那么这种行为将是没有记录的,这意味着如果我愿意的话,我有权让绿色恶魔从你的耳朵里飞出来,但我已经加入了前提条件,所以你会在测试中发现它(如果你的测试。关于函数退出时的状态的断言意味着您可以确定函数退出时的状态符合前提条件或者您需要聘请新的测试工程师,因为您现在的代码没有正确执行代码。

  2. 其他先决条件,例如循环变体和不变量(看看它们) 。
    up)是断言的第二种用途,它是为了帮助确保你没有搞砸你的逻辑(尽管你可能仍然依赖于那个测试人员)。作为副产品,它们有助于理解代码和您所做的假设,有点像可执行注释。

A lot of heat (and limited light) on this.

Here's my POV as someone used to working with safety-critical systems (ones that will only kill a few hundred people if they go wrong and you are lucky).

Assertions (in all languages, not just Java) are a development tool for helping ensure correctness of program logic, that should never be enabled in production code. They serve two purposes:

  1. Documentation. Assertions relating to the state when a function is called ("preconditions") document "don't you dare call this function if you're not in this state. If I hadn't put this precondition in, then the behaviour would be undocumented, which means I'd be entitled to make green demons fly out of your ears if I wanted to. But I've put in the precondition, so you'll catch it in testing (if your testing is any good at all). Assertions about the state on exit from a function mean you can either damn sure of the state on exit from the function matches the precondition or you need to get a new test engineer because your present one isn't exercising the code properly.

  2. Other preconditions such as loop variants and invariants (look 'em
    up) are the second use of assertions, which is to help you make sure you haven't screwed up your logic (though you might still be dependent on that tester). And as a by-product, they help understand the code and the assumptions you have made, a bit like executable comments.

眼眸 2024-09-07 03:14:34

我不知道Java中这个功能背后的历史是什么。但我有一个关于断言可以用来做什么的想法,我认为这个线程中没有提出过。

假设您有一个打开的枚举:(†)

public enum Veggie {
    CAULIFLOWER,
    CARROT,
}

// Another class
switch (veggie) {
    case CAULIFLOWER: value = 5;
    case CARROT: value = 3;
}

†:也许好的 Java 编译器/工具会静态捕获丢失的枚举值。在这种情况下,想象一下您发现了一些错误并修复了它。然后,您可以使用下面描述的技术在代码中添加回归测试。


对此没有好的默认情况。因此,您确实想确保涵盖所有值。您希望确保在添加新的枚举变体时更新 switch 块:

public enum Veggie {
    CAULIFLOWER,
    CARROT,
    TOMATO,
}

但事实证明,当用户在 Web 应用程序中加载特定视图时,会立即调用此代码。尽管出现此错误,视图仍然可以保留:这将是一个错误,但不值得干扰视图的加载(假设用户只是看到了一些错误的数字)。因此,您只需记录一条警告:

switch (veggie) {
    case CAULIFLOWER: value = 5;
    case CARROT: value = 3;
    default: nonExhaustiveMatchOnEnum();
// […]
public static void nonExhaustiveMatchOnEnum() {
    String errorMessage: "Missing veggie";
    logger.warn(errorMessage);
}

但是您确实希望在开发代码时立即失败 - 与 Web 应用程序的用户不同,开发人员可以在五分钟内修复此类错误。

因此,您只需添加一个 assert false

public static void nonExhaustiveMatchOnEnum() {
    String errorMessage: "Missing veggie";
    assert false : errorMessage;
    logger.warn(errorMessage);
}

现在应用程序将在本地崩溃,但只会在生产中记录警告(假设您在本地使用 java -ea 但不在生产中使用)。

I don’t know what the history is behind this feature in Java. But I have an idea about what assert can be used for that I don’t think has been brought up in this thread.

Say you have an enum which you switch on:(†)

public enum Veggie {
    CAULIFLOWER,
    CARROT,
}

// Another class
switch (veggie) {
    case CAULIFLOWER: value = 5;
    case CARROT: value = 3;
}

†: Maybe good Java compilers/tooling will catch missing enum values statically. In that case just imagine that you find some bug and you fix it. Then you can add a regression test in the code proper using the technique described below.


There’s no good default case for this. So you really want to make sure that you cover all values. You want to make sure that you update the switch block whenever you add a new enum variant:

public enum Veggie {
    CAULIFLOWER,
    CARROT,
    TOMATO,
}

But it turns out that this code is called right when the user loads a particular view in your web application. And the view can persist in spite of this error: it would be a bug but it’s not worth interfering with the loading of the view (let’s say that the user is just presented with a few wrong numbers). So you just log a warning:

switch (veggie) {
    case CAULIFLOWER: value = 5;
    case CARROT: value = 3;
    default: nonExhaustiveMatchOnEnum();
// […]
public static void nonExhaustiveMatchOnEnum() {
    String errorMessage: "Missing veggie";
    logger.warn(errorMessage);
}

But you do want this fail immediately when you are developing the code—you the developer can fix such a bug in five minutes, unlike the users of your web application.

So you can just add an assert false:

public static void nonExhaustiveMatchOnEnum() {
    String errorMessage: "Missing veggie";
    assert false : errorMessage;
    logger.warn(errorMessage);
}

Now the app will crash locally but only log a warning on production (assuming you use java -ea locally but not in production).

数理化全能战士 2024-09-07 03:14:34

基本上,“assert true”将通过,“assert false”将失败。让我们看看这是如何工作的:

public static void main(String[] args)
{
    String s1 = "Hello";
    assert checkInteger(s1);
}

private static boolean checkInteger(String s)
{
    try {
        Integer.parseInt(s);
        return true;
    }
    catch(Exception e)
    {
        return false;
    }
}

Basically, "assert true" will pass and "assert false" will fail. Let's looks at how this will work:

public static void main(String[] args)
{
    String s1 = "Hello";
    assert checkInteger(s1);
}

private static boolean checkInteger(String s)
{
    try {
        Integer.parseInt(s);
        return true;
    }
    catch(Exception e)
    {
        return false;
    }
}
不打扰别人 2024-09-07 03:14:34

assert 是一个关键字。它是在 JDK 1.4 中引入的。有两种类型的 assert

  1. 非常简单的 assert 语句
  2. 简单的 assert 语句。

默认情况下,所有 assert 语句都不会被执行。如果 assert 语句收到 false,那么它将自动引发断言错误。

assert is a keyword. It was introduced in JDK 1.4. The are two types of asserts

  1. Very simple assert statements
  2. Simple assert statements.

By default all assert statements will not be executed. If an assert statement receives false, then it will automatically raise an assertion error.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文