Java中的assert
关于Java断言
Java assert关键字允许开发人员快速验证程序的某些假设或状态。
Java assert关键字是在Java 1.4中引入的,所以它已经存在很长一段时间了。然而,它仍然是一个鲜为人知的关键字,它可以极大地减少样板文件并使我们的代码更具可读性。
例如,在我们的代码中,我们经常需要验证某些可能阻止应用程序正常工作的条件。通常我们会这样写:
Connection conn = getConnection();
if(conn == null) {
throw new RuntimeException("Connection is null");
}
使用断言,我们可以用一个assert语句来代替if和throw语句。
启用Java的断言
因为Java断言使用assert关键字,所以不需要导入库或包。
注意,在Java 1.4之前,使用单词“assert”来命名变量、方法等是完全合法的。在使用较旧的代码和较新的JVM版本时,这可能会造成命名冲突。
因此,为了向后兼容性,JVM在默认情况下禁用断言验证。必须使用-enableassertions命令行参数或者它的简写-ea来显式地启用它们:
java -ea com.baeldung.assertion.Assertion
此时我们为所有类启用了断言。
我们还可以为特定的包和类启用断言:
java -ea:com.baeldung.assertion... com.baeldung.assertion.Assertion
在这里,我们为com.baeldung.assertion中的所有类启用了断言。
同样,可以使用*-disableassertions命令行参数或其简写-da*来禁用特定的包和类。我们也可以同时使用这四个参数。
使用Java断言
要添加断言,只需使用assert关键字并给它一个布尔条件:
public void setup() {
Connection conn = getConnection();
assert conn != null;
}
Java还为断言提供了第二种语法,它接受一个字符串。如果一个错误被抛出,它将用于构造AssertionError:
public void setup() {
Connection conn = getConnection();
assert conn != null : "Connection is null";
}
从代码可知,Assert断言的表达式基本分为两类
Assert.方法名 (布尔表达式) : 这类方法都标记过时, 都调用第二类方法
Assert.方法名 (布尔表达式,错误提示信息) : 这类方法, 如果布尔表达式不满足,会抛出异常,并将异常信息封装.
在这两种情况下,代码都在检查到外部资源的连接是否返回非空值。如果该值为null, JVM将自动抛出AssertionError。
在第二种情况下,异常将具有附加的细节,这些细节将显示在堆栈跟踪(stack trace)中,并可以帮助程序员调试问题。
让我们看看运行启用了断言的类的结果:
Exception in thread "main" java.lang.AssertionError: Connection is null
at com.baeldung.assertion.Assertion.setup(Assertion.java:15)
at com.baeldung.assertion.Assertion.main(Assertion.java:10)
AssertionError类继承了Error类,而Error类本身继承了Throwable。这意味着AssertionError是一个非检查的异常(unchecked exception)。
因此,使用断言的方法不需要声明它们,而且进一步的调用代码不应尝试捕捉抛出的AssertionError。
AssertionErrors用于指出在应用程序中不可恢复的出错情况,因此不要试图处理它们或尝试恢复。
总结
关于断言,要记住的最重要的一点是,它们是可以禁用的,所以永远不要假设它们会被执行。
因此,在使用断言时要记住以下几点:
· 在适当的时候,总是检查null值和空选项
· 避免使用断言检查public方法的输入,而是使用检查的异常,如IllegalArgumentException或NullPointerException
· 不要在断言条件中调用方法,而是将方法的结果赋给局部变量,并在断言中使用该变量
· 断言非常适合于代码中永远不会执行的地方,比如switch语句的默认情况(default case)或永远不会结束的循环之后
· assert不是一个仓促拼凑起来的宏,为了不在程序的Debug版本和Release版本引起差别,assert不应该产生任何副作用。所以assert不是函数,而是宏。程序员可以把assert看成一个在任何系统状态下都可以安全使用的无害测试手段。
Spring中的assert
Spring Assert类帮助我们校验参数。通过使用Assert类方法,我们可以写出我们认为是正确的假设,反之,会抛出运行时异常。
每个Assert的方法可以与java assert表达式进行比较。java assert表达式在运行时如果条件校验失败,则抛出Error,有趣的是,这些断言可以被禁用。
Spring Assert的方法有一些特点:
- 都是static方法
- 抛出IllegalArgumentException 或 IllegalStateException异常
- 第一个参数通常是需验证的对象或逻辑条件
- 最后参数通常是异常消息,用于验证失败时显示
- 消息可以作为String参数或Supplier 参数传输
尽管Spring Assert与其他框架的名称类似,如JUnit或其他框架,但其实没有任何共同之处。Spring Assert不是为了测试,而是为了调试。
使用示例
让我们定义Car类,并有public方法drive():
public class Car {
private String state = "stop";
public void drive(int speed) {
Assert.isTrue(speed > 0, "speed must be positive");
this.state = "drive";
// ...
}
}
我们看到speed必须是正数,上面一行简短的代码用于检测条件,如果失败抛出异常:
if (!(speed > 0)) {
throw new IllegalArgumentException("speed must be positive");
}
每个Assert的方法包含大概类似上面的条件代码块,校验失败抛出运行时异常,应用程序不期望恢复。
如果我们尝试带负数参数调用drive方法,会抛出IllegalArgumentException异常:
Exception in thread "main" java.lang.IllegalArgumentException: speed must be positive
逻辑断言
isTrue()
上面已经看到示例,其接受布尔条件,如果条件为假抛出IllegalArgumentException 异常。
state()
该方法与isTrue一样,但抛出IllegalStateException异常。
如名称所示,通常用在因对象的非法状态时,方法不能继续执行。假设骑车运行是不能加油,我们可以使用state方法断言:
public void fuel() {
Assert.state(this.state.equals("stop"), "car must be stopped");
// ...
}
当然,我们能使用逻辑断言验证所有场景。但为了更好的可读性,我们可以使用其他的断言,使代码表达性更好。
对象和类型断言
notNull()
通过notNull()方法可以假设对象不null:
public void сhangeOil(String oil) {
Assert.notNull(oil, "oil mustn't be null");
// ...
}
isNull()
另外一方面,我们能使用isNull()方法检查对象为null:
public void replaceBattery(CarBattery carBattery) {
Assert.isNull(
carBattery.getCharge(),
"to replace battery the charge must be null");
// ...
}
isInstanceOf()
使用isInstanceOf()方法检查对象必须为另一个特定类型的实例:
public void сhangeEngine(Engine engine) {
Assert.isInstanceOf(ToyotaEngine.class, engine);
// ...
}
示例中,ToyotaEngine 是类 Engine的子类,所以检查通过.
isAssignable()
使用Assert.isAssignable()方法检查类型:
public void repairEngine(Engine engine) {
Assert.isAssignable(Engine.class, ToyotaEngine.class);
// ...
}
这两个断言代表 is-a 关系.
文本断言
通常用来检查字符串参数。
hasLength()
如果检查字符串不是空符串,意味着至少包含一个空白,可以使用hasLength()方法:
public void startWithHasLength(String key) {
Assert.hasLength(key, "key must not be null and must not the empty");
// ...
}
hasText()
我们能增强检查条件,字符串至少包含一个非空白字符,可以使用hasText()方法:
public void startWithHasText(String key) {
Assert.hasText(
key,
"key must not be null and must contain at least one non-whitespace character");
// ...
}
doesNotContain()
我们能通过doesNotContain()方法检查参数不包含特定子串:
public void startWithNotContain(String key) {
Assert.doesNotContain(key, "123", "key mustn't contain 123");
// ...
}
Collection和map断言
Collection应用notEmpty()
如其名称所示,notEmpty()方法断言collection不空,意味着不是null并包含至少一个元素:
public void repair(Collection<String> repairParts) {
Assert.notEmpty(
repairParts,
"collection of repairParts mustn't be empty");
// ...
}
map应用notEmpty()
同样的方法重载用于map,检查map不null,并至少包含一个entry(key,value键值对):
public void repair(Map<String, String> repairParts) {
Assert.notEmpty(
repairParts,
"map of repairParts mustn't be empty");
// ...
}
数组断言
notEmpty()
notEmpty()方法可以检查数组不null,且至少包括一个元素:
public void repair(String[] repairParts) {
Assert.notEmpty(
repairParts,
"array of repairParts mustn't be empty");
// ...
}
noNullElements()
noNullElements()方法确保数组不包含null元素:
public void repairWithNoNull(String[] repairParts) {
Assert.noNullElements(
repairParts,
"array of repairParts mustn't contain null elements");
// ...
}
注意,如果数组为空检查可以通过,只要没有null元素。
参考链接:
https://blog.csdn.net/neweastsun/article/details/80152756
https://zhuanlan.zhihu.com/p/265444322