Java的异常体系结构
Java异常体系的根类是 Throwable
, 所以当写在java代码中写throw
抛出异常时,后面跟的对象必然是Throwable
或其子类的对象。
其中Exception
异常是指一些可以恢复的异常, 例如常见的NullPointerException
空指针异常。
Error
指的是一些致命的错误,无法通过程序代码手段恢复的异常,例如OutOfMemoryError
内存溢出错误。
unchecked异常
在上图中除了RuntimeException
、Error
及其子类都是属于unchecked
的异常类型外,其他的都是受编译器checked
检查的异常。
unchecked
不受编译器检查的异常, 是因为这些错误在程序运行过程中是可以通过编程手段去控制住的,
例如常见的NullPointerException
空指针异常和IndexOutOfBoundsException
数组下标越界的异常,这些都可以事先使用if (xx != null)
以及 if (xxx.size() > i)
来控制,
或者就是完全无法通过程序手段控制,
例如OutOfMemoryError
内存溢出异常和*Error
栈溢出异常,这种Error
因为无法通过代码层面if
就能避免的,所以也属于unchecked
。
checked异常
checked
在编译过程中受到编译器的检查,如果程序没有对该异常做catch
处理或者向上一层抛出的话,程序将无法编译通过,
常见的checked
异常有FileNotFoundException
文件不存在异常等,因为这种异常在编写阶段就可以预见,例如这个文件极有可能是不存在的,所以这种异常必须要抛出并要求程序作出处理。
总结
Throwable
任何异常/错误的祖先类,属于checked
异常。
Exception
异常,可以从异常中恢复执行的异常,属于checked
异常。
RuntimeException
异常,预料之外的异常例如空指针、数组越界,属于unchecked
异常。
...Exception
除了RuntimeException
及其子类是unchecked
异常,其他的Exception
类都是checked
异常。
Error
错误,致命问题,无法从错误中恢复, 也属于unchecked
异常。
在开发过程中,如果一些可以预料的到的错误抛出异常时,尽量抛出checked
异常,例如那个文件、某个数据一定可能会不存在的情况下,就要提示该方法的调用者,需要对这种情况进行处理,
如果是一些预料之外的异常,则可以使用RuntimeException
,例如某个值规定一定是必须不为空,但是程序判断时为空了,则要进行RuntimeException
的抛出。
面试题
什么是checked/unchecked/runtime exception?
checked exception
指的是除了Error
、Runtime Exception
及其子类之外的所有异常,
unchecked exception
指的是Error
、Runtime Exception
及其子类的异常,
runtime exception
属于unchecked
异常。
try/catch/finally的执行顺序
try
用于包含运行时的代码块,第一步执行,
catch
用于捕获代码运行时可能发生的异常,第二步执行
当代码块执行到某一步发生错误时,后面的代码将不会进行执行,
而是跳转到catch
的代码块中,catch
顺序由上而下,以第一个可以捕获到当前异常的catch
进行执行其中的内容,
finally
是程序不管有没有发生异常,这里的代码最终一定会执行,所以是第三步执行。
在finally中return数据会怎么样
由于finally
在不管什么情况下都会执行,所以finally
中的return或覆盖掉其他地方的return,最终以finally
返回的为主,图中最终返回结果是2。
throw和throws的区别
throw
是用于在程序运行过程中,如果碰到了觉得不正确的值或者结果,可以通过throw new XXX()
来抛出一个异常,终止当前程序的继续执行。
throws
是用于在方法签名上指出该方法将抛出什么异常,告诉调用者,调用此方法可能会产生的异常,让调用者做相应的处理。
final、finally、finalize的区别
final
用于修饰类、方法、变量,在类上该类不可被继承,在方法上,该方法不可被重写,在变量上,该变量引用不可被更改。
finally
用于在try
语句中,意味着finally
包含的代码必须执行,不管有没有异常。
finalize
是所有对象的一个方法,在该对象被回收前,将会被垃圾回收器调用,但是只会调用一次,一般可以在该方法中挽救当前将被回收的对象,例如使用一个变量引用当前对象,但是这种方式不可取,因为垃圾回收器不会保证该方法被执行完毕,可能正在赋值的过程中该对象就被回收了,
这个方法类似C++
的析构函数,但是不稳定,官方也不推荐使用,只是因为历史原因,为了让C++
程序员更适应Java
作出的一个妥协。