软件工程领域的大师级人物 Robert C. Martin在《Clean Code》中讲道: 错误处理是十分必要的,但是如果对错误处理使用不当则会让代码变得十分臃肿,让阅读者看不清代码的逻辑,更严重的是,这也会让程序变得十分脆弱。
Exception
而不是返回码返回码是一个历史遗留问题,在以前的没有 Exception
的语言(比如c语言)中,它是有效且必要的,但是在有 Exception
的语言中使用返回码是没有任何益处的。
对于使用返回码的函数,调用者在得到调用结果(这里是返回码)之后要立即去验证返回码,这对于代码的可读性和结构的合理性都是极大的挑战,使用「异常处理」能让业务逻辑和错误处理在代码结构上分离,代码的结构和逻辑会更清晰。
========华丽的分割线========
现在我们就来实测下这种抛异常的策略,是否会影响性能: 代码【源码已上传到github,地址在文末】:
/**
* 相同条件下,同样的业务逻辑和IO下,比较抛异常和不抛异常场景下,
* 在性能上有什么区别
*/
@Test
public void givenTwoScenario_whenOneHasExceptionAndAnotherNot_thenGetTheElapsedTime() {
StopWatch stopWatch = new StopWatch("OneHasExceptionAndAnotherNot");
stopWatch.start("hasNoException");
int length = 1000000;
for (int i = 0; i < length; i++) {
doBizHasNoException(String.valueOf(i));
}
stopWatch.stop();
stopWatch.start("hasException");
for (int i = 0; i < length; i++) {
doBizHasException(String.valueOf(i));
}
stopWatch.stop();
log.info("{}", stopWatch.prettyPrint());
}
/**
* 没有抛异常的场景【也打印个日志】
*
* @param i
*/
public void doBizHasNoException(String i) {
try {
log.info("result:{}", i);
} catch (Exception e) {
log.warn("{}", e.getMessage());
}
}
/**
* 抛异常的场景【打印个日志】
*
* @param i
*/
public void doBizHasException(String i) {
try {
throw new IllegalArgumentException(i);
} catch (Exception e) {
log.warn("{}", e.getMessage());
}
}
实际执行效果:
10:19:26.327 [main] INFO com.tangcheng.learning.syntax.PerformanceAnalysisUseExceptionTest - StopWatch 'OneHasExceptionAndAnotherNot': running time (millis) = 17315
-----------------------------------------
ms % Task name
-----------------------------------------
05510 032% hasNoException
11805 068% hasException
可以看到,抛异常场景下,会更耗性能。 这个结论是不是很逆天,很不可思议。
当时也觉得百思不得其解,后来偶然在《极客时间》上听了 郑雨迪 讲的《深入拆解Java虚拟机》,感觉豁然开朗,那就不班门弄斧了,直接看看大佬来自jvm的视角:
异常实例的构造十分昂贵。
这是由于在构造异常实例时,Java虚拟机便需要生成该异常的栈轨迹(stack trace)。
该操作会逐一访问当前线程的Java栈帧,并且记录下各种调试信息,包括栈桢所指向方法的名字,方法所在的类名、文件名,以及在代码中的第几行触发该异常。
上面的文本不能copy,敲着比较费劲,那就直接截图了啊:
评论永远有经典:
测试用例源码路径: https://github.com/helloworldtang/spring-boot-cookbook/blob/v1.0.2-thymeleaf/learning-demo/src/test/java/com/tangcheng/learning/syntax/PerformanceAnalysisUseExceptionTest.java