Throwable
所有异常的父类,其下有两个分支,一个是 Error,一个是 Exception;
Throwable 类中有三个常用的方法:
- public void printStackTrace() 输出异常信息以及堆栈跟踪信息;
- public String getMessage() 输出异常内容;
- public String toString() 输出异常类+异常内容;
Error
是程序不可修复的错误,例如内存溢出,资源耗尽。所以异常处理基本上不考虑 Error 的情况。
Exception
是程序可以修复的错误。Exception 又分为运行时异常和编译异常。
Exception 有个子类叫 RuntimeException,它是所有运行异常的父类;Exception 剩下的直接子类都属于编译异常;
运行时异常:
有部分异常是当程序运行过程中,才抛出来的。例如我们熟悉的 NullPointerException、IndexOutOfBoundsException,常常由于程序员的疏忽,或者一些不可预知的情况,控制台能看到这些异常。
所以运行异常有个特点:
方法内部如果抛出的是运行时异常,方法上既无需声明,程序员也无需捕捉。为什么呢?
因为运行时异常属于必须修复的错误。一旦发生,程序员需要修改源代码,修复这种运行时异常,所以既不用捕捉,也无需抛出声明。毕竟一旦发生,后面的代码已经没有意义了。
比如经常遇到一个变量报了一个空指针异常,后面的业务中还需要用到这个变量,但是这个变量都不存在,所以后面的代码也就没有了执行下去的意义。
编译异常:
又叫受检查异常,程序在编译阶段,编译器会检查这类异常是否进行了异常处理。
像我们调用过的日期转换 public Date parse(String source) throws ParseException,或者JSONObject 里面的 prase 方法。我们在调用这些方法的时候,编译器就会报错提醒我们有异常需要处理。
编译异常有两种处理方式。一种是在方法后面添加 throws xxxException,将异常处理抛给调用者处理;另外一种是方法本身通过 try…catche来处理异常。
throws
用于方法上,例如 public Date parse(String source) throws ParseException,在方法的最后面加上 throws xxxException,这种方式,异常一但抛出,方法本身不需要处理异常,由方法的调用者来选择,调用者可以继续 throws 或则用 try…catch捕捉。如果一直 throws 的话,最终的调用者是 JVM
try…catch…finally
try 代码块里面是可能抛异常的代码;
catch 代码块,如果真的有异常了,就会进到这个代码块;
finally 代码块,必须执行的代码块,无论是否抛异常;除非代码调用了 system.exit();通常用来关闭资源,比如说 IO 流、文件流、网络流;
注意事项:
try 中的声明的变量有作用域问题;
多 catch 处理异常的时候,平级关系的异常没有顺序,有上下级关系的,上级放后面;
例如:
catch(NullPointerException e){}catch(RuntimeException e){}catch(ParseException e){}
ParseException 是编译异常,跟前两个异常没有关系,所以放哪都行;
NullPointerException 是 RuntimeException 的子类,但前者不能放在后者后面,一是程序不允许,二是如果 RuntimeException 放前面,只要是 RuntimeException 的子类都会进去,当真的是 NullPointerException 的时候,还是会进 RuntimeException,所以 catch(NullPointerException e){} 就没有意义了。
throws && try…catch 很重要的区别
使用 throws 的话,假设方法体有 10 行代码,当在第五行代码抛出了异常,那么第 6 行代码及往后的代码都不会执行了。
使用 try…catch 的话,
假设 try 代码块中有 5 行,当在第二行发生了异常,try 中剩下的 3 行代码将不执行,会进入到 catch 代码块;
只要捕捉并且处理完异常,跳出 try…catch 后面剩下多少行代码都将继续执行。
子父类异常
- 如果父类的方法上有声明异常,子类重写父类的方法后,重写的方法上可以不声明异常;如果要重写的方法要声明异常,异常不能大于父类的异常;
- 如果父类的方法上没有抛异常,子类重写父类的方法,也不能抛异常,只能通过 try…catch 处理异常
throw
throw new xxxException(“异常信息”)
手动抛出异常,可以隐藏具体异常信息,可以很友好的告诉调用者括号里面的 异常信息。
自定义异常
自定义的异常类需要继承 Exception 及其子类或者 RuntimeException 及其子类。
自定义异常一般需要提供两个构造方法,一个是无参的构造方法,另一个是传一个String message 的有参构造方法,并且调用super(message);
自动资源管理
我们在 finally 代码块,通常用来关闭资源,有时可能要关闭的资源很多,毕竟每关闭一个资源都有一个 try…catch;
所以在 JDK7 提供了一个自动资源管理的技术。可以替代 finally 代码块,优化代码结构,提高可读性;
在 try 语句后面填一对小括号(),括号里面声明或初始化资源语句,如有多条语句则用分号隔开;
例如
try (FileInputStream readfile = new FileInputStream(“readme.txt”);
InputStreamReader ir = new InputStreamReader(readfile);
BufferedReader in = new BufferedReader(ir)) { } catche(Exception e){}
括号里声明初始化了三个输入流,这就是自动资源管理技术,使用此技术之后,可以不写 finally,不需要自己 close 资源,释放资源的过程就交给了 JVM;
不过要注意的是,所有可以自动管理的资源需要实现 AutoCloseable接口;
看个例子:
public abstract class InputStream implements Closeable{}
public interface Closeable extends AutoCloseable{}
public interface AutoCloseable{}