Java assert关键字有什么作用,什么时候应使用?


601

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


8
在现实生活中,您几乎看不到它们。猜想:如果使用断言,则必须考虑三种状态:断言通过,断言失败,断言被关闭,而不仅仅是两个。并且assert默认情况下处于关闭状态,因此这是最有可能的状态,很难确保为您的代码启用了它。总而言之,断言是一种过早的优化,使用范围有限。正如您在@Bjorn的答案中所看到的,甚至很难提出一个用例,您不想一直使断言失败。
伊斋

35
@Yishai: “您必须考虑...断言已关闭” 如果您需要这样做,那么您做错了。“断言是对有限使用的过早优化”,这几乎是不可能的。这是Sun的观点:“ 在Java技术中使用断言 ”,这也读起来不错:“ 使用断言(即断言声明)进行编程的好处
David Tonhofer 2014年

5
@DavidTonhofer,在现实生活中您几乎看不到它们。这是可验证的。根据需要检查尽可能多的开源项目。我并不是说您不验证不变式。那不是同一回事。换一种方式。如果断言是如此重要,为什么默认情况下将其关闭?
Yishai 2014年

17
参考资料,FWIW:软件断言与代码质量之间的关系“我们还将断言的有效性与流行的错误发现技术(例如源代码静态分析工具)进行比较。我们从案例研究中观察到,随着断言密度的增加在文件中,故障密度在统计上显着降低。”
David Tonhofer 2014年

4
@DavidTonhofer David,我认为您对断言的热爱是你们正在做的一种非常特殊的编程类型,在我的领域中,由于任何原因退出该程序的Web应用程序都是最大的不对-我个人从来没有除了单元/
完整性

Answers:


426

断言(通过assert关键字)是在Java 1.4中添加的。它们用于验证代码中不变式的正确性。切勿在生产代码中触发它们,并表示存在错误或滥用代码路径。可以在运行时通过命令-ea上的选项将其激活java,但默认情况下未启用它们。

一个例子:

public Foo acquireFoo(int id) {
  Foo result = null;
  if (id > 50) {
    result = fooService.read(id);
  } else {
    result = new Foo(id);
  }
  assert result != null;

  return result;
}

71
实际上,Oracle告诉您不要使用它assert来检查公共方法参数(docs.oracle.com/javase/1.4.2/docs/guide/lang/assert.html)。那应该抛出一个Exception而不是杀死程序。
2013年

10
但是您仍然不解释它们为什么存在。为什么不能执行if()检查并引发异常?
El Mac'3

7
@ElMac-断言用于循环的开发/调试/测试部分-不用于生产。一个if块在prod中运行。简单的断言不会花费很多,但是执行复杂数据验证的昂贵断言可能会使您的生产环境瘫痪,这就是为什么在这里关闭它们的原因。
hoodaticus

2
@hoodaticus您的意思仅仅是我可以打开/关闭产品代码的所有断言的事实?因为无论如何我都可以进行复杂的数据验证,然后使用异常进行处理。如果我有生产代码,那么我可以关闭复杂的(可能是昂贵的)断言,因为它应该工作并且已经过测试?从理论上讲,他们不应该降低该程序的质量,因为这样您无论如何都会遇到问题。
El Mac

8
This convention is unaffected by the addition of the assert construct. Do not use assertions to check the parameters of a public method. An assert is inappropriate because the method guarantees that it will always enforce the argument checks. It must check its arguments whether or not assertions are enabled. Further, the assert construct does not throw an exception of the specified type. It can throw only an AssertionError. docs.oracle.com/javase/8/docs/technotes/guides/language/…–
Bakhshi

325

假设您应该编写一个程序来控制核电站。很明显,即使是最轻微的错误也可能导致灾难性的结果,因此您的代码必须没有错误(假设出于参数考虑,JVM是没有错误的)。

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

这个想法是基于合同设计(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;
}

如果此功能现在失败,您将注意到它。您将知道代码中存在问题,知道问题出在哪里,并且知道是什么原因引起的(类似于Exceptions)。更重要的是:您会在发生错误时停止正确执行,以防止任何其他代码使用错误的值工作,并可能损坏其控制的内容。

Java异常是一个类似的概念,但是它们无法验证所有内容。如果要进行更多检查(以执行速度为代价),则需要使用断言。这样做会使您的代码膨胀,但是最终您可以在出乎意料的短开发时间内交付产品(越早修复错误,成本越低)。另外:如果您的代码中有任何错误,您将对其进行检测。没有任何漏洞可以通过,并在以后引起问题。

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


29
我选择此示例是因为它很好地呈现了看似没有错误的代码中的隐藏错误。如果这与别人提出的内容相似,那么他们可能有相同的想法。;)
TwoThe

8
您选择断言是因为断言为false时它将失败。一个if可以有任何行为。打边缘情况是单元测试的工作。使用按合同设计可以很好地指定合同,但是与现实生活中的合同一样,您需要进行控制以确保遵守合同。带有断言的情况下,将插入看门狗,当不遵守合同时,您会看到您。可以将其想象为每次您做的事超出合同规定或违反合同,然后将您送回家时,a叫“错”的ONG律师,这样您就无法继续工作并进一步违反合同!
埃里克

5
在这种简单情况下是必需的:否,但是DbC定义必须检查每个结果。想象一下,现在有人将功能修改为更复杂的功能,然后他也必须修改后检查,然后突然变得有用。
TwoThe

4
很抱歉要复活,但我有一个具体问题。@TwoThe所做的与不使用assert仅向new IllegalArgumentException消息中抛出a 和有什么区别?我的意思是,除了throws要在方法声明和代码中添加o 来管理其他地方的异常。为什么要assert抛出新的异常?还是为什么不使用if代替assert?真的不能得到这个:(
Blueriver 2014年

14
-1:如果a可以为负,则检查溢出的断言是错误的。第二个断言是没有用的。对于int值,总是+ a-b-b == a。仅当计算机从根本上损坏时,该测试才会失败。为了避免这种情况的发生,您需要检查多个CPU之间的一致性。
凯文·克莱恩

63

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

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

通常,在这样的方法中,小部件绝对不应为null。因此,如果它为null,则您的代码中存在一个需要跟踪的错误。但是上面的代码永远不会告诉您这一点。因此,在精心编写“安全”代码的过程中,您还隐藏了一个错误。编写这样的代码会更好:

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

这样,您一定会早日发现此错误。(在合同中指定此参数永远不应为null也很有用。)在开发过程中测试代码时,请确保打开断言。(而且说服您的同事也经常这样做很困难,我觉得这很烦人。)

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

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

这样,您的同事会很高兴为生产代码提供了null检查,但是在开发过程中,当widget为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;
}

equals()如果thisValue不为null ,则此代码委派方法的工作。但是它假定该equals()方法equals()通过正确处理null参数正确履行了契约。

一位同事反对我的代码,告诉我我们的许多类都具有equals()无法测试null的错误方法,因此我应该将该检查放入该方法中。这是否明智还是值得商bat的,还是应该强制执行此错误,以便我们找出并修复该错误,但是我推迟到同事那里并进行了空检查,并在上面加上了注释:

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()在开发中发现任何错误的方法。

另外,您应该通过放置将失败的临时断言来测试断言策略,以便可以确定是通过日志文件还是通过输出流中的堆栈跟踪来通知您。


关于“隐藏错误”以及断言如何在开发过程中暴露错误的要点!
nobar

这些检查都不慢,因此没有理由在生产中将其关闭。应该将它们转换为日志记录语句,以便您可以检测出在“开发阶段”中没有出现的问题。(实际上,无论如何都没有开发阶段。当您决定完全停止维护代码时,开发就会结束。)
Aleksandr Dubinsky

20

很多很好的答案说明了assert关键字的作用,但是很少回答真正的问题:“何时assert在现实生活中使用关键字?”

答案:几乎永远不会

断言作为一个概念是很棒的。好的代码包含很多if (...) throw ...语句(它们的亲戚喜欢Objects.requireNonNullMath.addExact)。但是,某些设计决策极大地限制了assert 关键字本身的用途。

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

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

因此,if (...) throw ...应该首选使用,就像检查公共方法的参数值和throwing一样IllegalArgumentException

有时,人们可能会倾向于编写不变的检查,这确实需要花费很长的时间来处理(并且经常被调用以使其变得重要)。但是,这样的检查会使测试变慢,这也是不希望的。这种耗时的检查通常被编写为单元测试。尽管如此,assert出于这个原因有时使用还是有意义的。

不要assert仅仅因为它比它更干净,更漂亮而使用它if (...) throw ...(我说这很痛苦,因为我喜欢干净漂亮)。如果您不能自已,并且可以控制应用程序的启动方式,则可以随时使用,assert始终在生产中启用断言。诚然,这是我倾向于做的。我正在推动龙目岛注释,这将使assert行为更像if (...) throw ...在这里投票。

(Rant:JVM开发人员是一堆糟糕的,过早优化的编码器。这就是为什么您听说Java插件和JVM中存在如此多的安全性问题的原因。他们拒绝在生产代码中包含基本的检查和断言,并且我们将继续付出代价。)


2
@aberglas一个包罗万象的条款是catch (Throwable t)。我们没有理由不尝试陷阱,日志,或重试/从OutOfMemoryError异常,Asse田,等恢复
亚历山大·杜宾斯基

1
我已从OutOfMemoryError中捕获并恢复。
MiguelMunoz

1
我不同意 我的许多断言用于确保正确调用我的API。例如,我可能编写了一个私有方法,仅当对象持有锁时才应调用它。如果另一个开发人员从未锁定对象的代码部分中调用该方法,则断言将立即告诉他们他们犯了错误。像这样的许多错误可以肯定地陷入开发阶段,并且断言在这些情况下非常有用。
MiguelMunoz '16

2
@MiguelMunoz在我的回答中,我说断言的想法很好。它的执行assert关键字不好。我将对答案进行编辑,以使其更清楚地表明我所指的是关键字,而不是概念。
Aleksandr Dubinsky

2
我喜欢它抛出AssertionError而不是Exception的事实。如果代码只能抛出类似IOException之类的东西,仍然有太多的开发人员还不了解他们不应该捕获Exception。我的代码中的错误被完全吞噬,因为有人捕获了Exception。断言不会陷入此陷阱。例外是您希望在生产代码中看到的情况。至于记录,即使错误很少见,您也应该记录所有错误。例如,您真的要让OutOfMemoryError传递而不记录它吗?
MiguelMunoz

14

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

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

只要您处理好每种情况,就可以了。但是总有一天,有人会将无花果添加到您的枚举中,而忘记将其添加到您的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;
}

4
这就是为什么应该启用警告并将警告视为错误的原因。任何中途的编译器都可以告诉您,如果仅允许您告诉您,您缺少枚举检查,它将在编译时这样做,这比(可能是一天)在编译时发现要好得多。运行。
Mike Nakis 2014年

9
为什么在这里使用断言而不是某种异常(例如非法参数异常)?
liltitus15年

4
AssertionError如果启用了断言,则会抛出一个(-ea)。生产中期望的行为是什么?在执行过程中无声的无操作和潜在的灾难?可能不是。我建议一个明确的throw new AssertionError("Missing enum value: " + fruit);
aioobe

1
仅抛出一个AssertionError有一个很好的论据。至于生产中的适当行为,断言的全部目的是防止这种情况在生产中发生。断言是用于捕获错误的开发阶段工具,可以很容易地从生产代码中将其删除。在这种情况下,没有理由将其从生产代码中删除。但是在许多情况下,完整性测试可能会使速度变慢。通过将这些测试放入生产代码中未使用的断言中,您可以自由编写详尽的测试,而不必担心它们会降低生产代码的速度。
MiguelMunoz '16

这似乎是错误的。恕我直言,您不应使用它,default以便编译器可以在遗失案件时向您发出警告。您可以return代替break(这可能需要提取方法),然后在切换后处理丢失的情况。这样,您既可以得到警告,也可以得到机会assert
maaartinus

12

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

断言的一个示例可能是检查是否以正确的顺序调用了一组特定的方法(例如,hasNext()在之前调用next()Iterator)。


1
您不必在ne​​xt()之前调用hasNext()。
DJClayworth 2010年

6
@DJClayworth:您也不必避免触发断言。:-)
Donal Fellows 2010年

8

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传递在命令行上,否则为假。

我们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在命令行上的系统属性throw new AssertionError()

2
那么该catch (Throwable t)子句也能够捕获断言吗?对我来说,将它们的效用限制在断言主体非常耗时的情况下,这种情况很少见。
Evgeni Sergeev 2015年

1
我不确定为什么它限制了断言的用处。除非在极少数情况下,否则您永远都不应捕获Throwable。如果确实需要捕获Throwable但不希望它捕获断言,则可以捕获第AssertionError一个并重新抛出它。
MiguelMunoz

7

来自Stack类的真实示例(来自Java文章中的Assertion

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

80
这在C语言中会被皱眉:断言实际上是永远不会发生的-弹出一个空栈应该抛出NoElementsException或类似的东西。请参阅Donal的回复。
Konerak 2010年

4
我同意。即使这是从官方教程中摘录的,这也是一个不好的例子。
DJClayworth 2010年


7
那里可能有内存泄漏。您应该设置stack [num] = null; 为了使GC正常工作。
H.Rabiee 2014年

3
我认为在私有方法中,使用断言是正确的,因为对于类或方法的故障有例外会很奇怪。在一个公共方法中,从外部某个地方调用它,您实际上无法确定其他代码如何使用它。它真的检查isEmpty()吗?你不知道
Vlasec 2014年

7

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

当您知道某件事是真实的时,为什么要断言?只有当一切正常工作时,这才是正确的。如果程序有缺陷,则可能实际上并非如此。在过程的较早阶段检测到这一点,可以让您知道出了点问题。

一个assert语句包含一个可选的沿着这条语句String的消息。

assert语句的语法有两种形式:

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

5

除了此处提供的所有出色答案外,官方Java SE 7编程指南还提供了一个非常简洁的使用手册assert;列举了几个示例,这些示例说明何时使用断言是一个好主意(重要的是,不好的主意),以及与引发异常有何不同。

链接


1
我同意。这篇文章有许多出色的例子。我特别喜欢一种确保仅在对象持有锁时才调用方法的方法。
MiguelMunoz

4

开发时断言非常有用。如果您的代码正常工作,则在某些事情无法发生时,可以使用它。它易于使用,并且可以永久保留在代码中,因为它将在现实生活中被关闭。

如果现实生活中可能会出现这种情况,那么您必须处理它。

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


1
为了能够在Eclipse IDE中断言,请按照tutoringcenter.cs.usfca.edu/resources/...
阿亚兹帕夏

我认为没有办法在Android中打开断言。这非常令人失望。
MiguelMunoz

3

这是我在服务器中为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. 有人问:如果生产中不需要断言,为什么不干完就把它们删除呢?因为当您开始使用下一个版本时,仍然需要它们。

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

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

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

这仅适用于三个值,但是如果可能值的数量变大,则HashSet版本将变得更加方便。我选择了HashSet版本来说明效率。


我强烈怀疑使用如此小的 a HashSet会带来比s更快的速度优势ArrayList。此外,集合和列表创建支配着查找时间。使用常量时它们很好。都说,+ 1。
maaartinus

都没错 我以这种低效的方式来说明我的观点,即断言可以放慢速度。可以提高这一效率,但是还有其他一些效率不高。史蒂夫·马奎尔(Steve Maguire)在一本非常出色的书,名为《编写坚实的代码》中,讲述了Microsoft Excel中的一个断言,用于测试新的增量更新代码,该代码跳过了不应更改的单元格。每次用户进行更改时,断言​​都会重新计算整个电子表格,以确保结果与增量更新功能相匹配。确实降低了调试版本的速度,但是他们尽早捕获了所有错误。
MiguelMunoz

完全同意。断言是一种测试-与普通测试相比,通用性较差,但是它们可以涵盖私有方法,并且编写起来便宜得多。我会尝试更多地使用它们。
maaartinus

2

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

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


2

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

使用断言有两种格式:

  1. 简单:例如。assert 1==2; // This will raise an AssertionError
  2. 更好:assert 1==2: "no way.. 1 is not equal to 2"; 这也会引发AssertionError,同时显示给定的消息,因此更好。尽管实际的语法是assert expr1:expr2expr2可以是返回值的任何表达式的地方,但我经常使用它来打印消息。

1

回顾一下(这对许多语言都是正确的,而不仅仅是Java):

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

无论您是否表示逻辑错误,“异常”都是一种处理各种错误情况的便捷方法,因为如果遇到了无法继续的错误情况,您可以简单地“将它们抛诸脑后,无论您身在何处,都希望其他人能随时“抓住”他们。控制权直接从引发异常的代码直接转移到捕手的手套,只需一步即可。(捕获器可以看到已发生的呼叫的完整回溯。)

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

异常可以很方便地使致命错误条件成为它们:“规则例外”。而且,对于它们来说,它们也应由代码路径来处理,该路径也是“规则的例外…… 飞球!”


1

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

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

那么,还剩下什么呢?为条件检查真的希望是真的。一个很好的例子是像RB-tree这样的数据结构的不变性。实际上,在ConcurrentHashMapJDK8中,对于来说有一些有意义的断言TreeNodes

  • 您确实不希望在生产中启用它们,因为它们很容易控制运行时间。
  • 您可能需要在测试期间打开或关闭它们。
  • 您一定要在处理代码时将其打开。

有时,支票并不是真的很贵,但同时,您肯定可以通过。在我的代码中,例如

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

我敢肯定,我刚刚创建的列表具有唯一的元素,但是我想对其进行记录并再次检查。


0

基本上,“ 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;
    }
}

-8

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

  1. 非常简单的assert陈述
  2. 简单的assert陈述。

默认情况下,assert将不执行所有语句。如果一条assert语句接收到错误,那么它将自动引发一个断言错误。


1
它没有提供任何现实生活中的例子,这是问题的目的
rubenafo

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.