之所以工作这么多年了,突然还写这么基础的问题。是因为多年前有次面试,被问到过这个问题。我说了自己的对异常的理解,面试官很不满意。但实际上到现在我也没觉得自己的理解有什么问题,反而觉得网上的千篇一律的解释非常模糊且有问题。所以这里再把自己想法汇总出来,让大家看看。
异常有两种:检查异常 和 非检查异常
非检查异常
如:空指针异常
表象上,没有强制让你捕获的异常
深层解释:完美的程序就不会有非检查异常(面试官觉得这个说法非常荒唐)
检查异常
如:IO异常
表象上,编译器强制让你捕获的异常
深层解释:让这个异常成为正常程序的一部分逻辑
但是以上说的都是“完美程序”,实际上由于人脑客观存在容易犯错误,容易忽视一些bug。
所以还存在一种“跑起来才发现的意想不到的异常”(就是几乎每天都在和程序员战斗的异常)。 这个不用单独列出来,因为被归到“非检查异常”里了。
说到这里你可能马上想到:这不就是“运行时异常”(RuntimeException)吗?不,还包括Error
非检查异常(如空指针),编译器为什么没强制你捕获?
因为在编程语言设计者看来。如果你的程序没有问题,那么这种错误就不应该存在。如果程序报错了,你必须让程序停下来,改好再运行。如果你不明就里的捕获并且也不处理,就是在掩盖自己的错误。程序看起来是不报错,但已经处于异常运行状态。
编译器特意不提醒你,就是让你的程序在出错时,控制台出现大片报错信息,以此警示:你的程序有问题,打回去重写。
相对应的,看检查异常(如IO异常)。这是你程序的问题吗,不,这是程序以外的世界造成的不可抗力因素。也就是说,即便你的程序完美无缺,外面给你断网,你的程序照样出问题。那我们怎么办呢? 答:把这种异常捕获,然后变成程序的一部分逻辑(if (正常)…else(断网)…)。
编译器强制你捕获,言外之意就是说:这段捕获异常的程序是正常的业务判断逻辑,你可别忘了!
所以很多人告诉初学者:异常捕获不应当作逻辑判断条件。我这里要反驳:检查异常就应该是正常业务逻辑的一部分
最后再来看看Error,这个被单独拿出来的异常。它属于什么呢?
它也属于非检查异常,因为它也是程序造成的。无论是你程序导致的jvm出问题,还是jvm本身的缺陷造成的,那都算程序的问题。
因为jvm也是一段程序。java程序员每天写的程序 其实都是在“完善jvm”,想想是不是。jvm+我们的程序 才是一个完整的程序【这种关系,就好像 war包之于tomcat】。
这样去理解,是不是编程的世界就很完美了。只要捕获了检查异常,无论外界如何变,都能保证我们的程序完美运行。
但这只是理论。现实中,就像我前面说的,人脑会犯错也是自然规律。事实上捕获非检查异常就是为了应对人脑的不完美。所以实际开发中,我们大量使用了非检查异常,防止出现意想不到的情况。所以正常的程序长这个样子:
try {
...
try {外部正常时...}
catch(检查异常) {外部出问题时...}
...
} catch(非检查异常) {上面这段程序有问题,快来看看...}