基本概念
- Java使用异常来提供一致性的错误报告模型;且可集中错误处理;且任务代码与异常代码分割开来,易于理解和维护
- 异常处理理论有终止模型、恢复模型两种。但恢复模型很难优雅地做到,并不实用,实际中大家都转向使用终止模型。
- 一个异常抛出后发生的两件事:① 使用new在堆上创建异常对象;② 异常处理机制开始接管流程(当前的执行流程被终止)
- 标准异常类均有两个构造器:① 默认构造器 ② 带字符串参数的默认构造器
- Throwable是异常类型的根类
- catch异常时,try中抛出的是子类异常,但catch的是基类异常也是OK,但若catch子类异常和基类异常的子句同时存在时,应将基类catch子句放在后面避免“屏蔽”现象发生
抛出异常 + 捕获异常
- 为什么要抛出异常?
因为在当前环境下无法获得必要的信息来解决问题,所能做的就是从当前环境跳出,并且把问题提交给上一级环境。
- 抛出异常(throw)
if(t == null) {
throw new NullPointerException();
}
- 捕获异常(try+catch)
try {
...
} catch( Type1 e1 ) {
// 处理Type1类型的异常代码
} catch( Type2 e2 ) {
// 处理Type2类型的异常代码
}
- 虽然上面的e1和e2在处理异常代码中可能用不到,但不能少,必须定义
- 异常发生时,异常机制搜寻参数与异常类型相匹配的第一个catch子句并进入
创建自定义异常
创建不带参数构造器的自定义异常类
// 自定义异常类
class SimpleException extends Exception {
}
----------------------------------------------
// 客户端代码
public class UseException {
public void fun() throws SimpleException{
System.out.println("Throw SimpleException from fun");
throw new SimpleException();
}
public static void main(String[] args) {
UseException user = new UseException();
try {
user.fun();
System.out.println("Don't caught it");
} catch (SimpleException e) {
System.out.println("Caught it");
}
}
}
----------------------------------------------
//输出
Throw SimpleException from fun
Caught it
创建带参数构造器的自定义异常类
//自定义异常类
class MyException extends Exception {
public MyException(){
}
public MyException(String msg){
super(msg);
}
}
----------------------------------------------
// 客户端代码
public class UseException {
public static void f() throws MyException{
System.out.println("Throwing MyException from f()");
throw new MyException();
}
public static void g() throws MyException{
System.out.println("Throwing MyException from g()");
throw new MyException("Exception in g()");
}
public static void main(String[] args) {
try {
f();
} catch (MyException e) {
e.printStackTrace(System.out);
}
try {
g();
} catch (MyException e) {
e.printStackTrace(System.out);
}
}
}
----------------------------------------------
//输出
Throwing MyException from f()
zhy.exception.ctor.MyException
at zhy.exception.ctor.UseException.f(UseException.java:12)
at zhy.exception.ctor.UseException.main(UseException.java:22)
Throwing MyException from g()
zhy.exception.ctor.MyException: Exception in g() // 此处即创建异常类型时传入的String类型
at zhy.exception.ctor.UseException.g(UseException.java:17)
at zhy.exception.ctor.UseException.main(UseException.java:28)
对于异常来说,最重要的部分就是类名,并且不带参数的异常类在大多数情况下已经够用了
捕获所有异常
try {
...
} catch( Exception e ) { // 填写异常的基类,该catch子句一般置于末尾
...
}
Exception类型所持有的方法:
1. String getMessage()
2. String getLocalizedMessage()
- void printStackTrace()
- void printStackTrace( PrintStream )
- void printStackTrace( javo.io.PrintWriter )
注意:从下往上每个方法比前一个提供更多的异常信息
栈轨迹
printStackTrace()方法所提供的栈轨迹信息可以通过getStackTrace()方法来Get
try {
throw new Exception();
} catch( Exception e ) {
for( StackTraceElement ste : e.getStackTrace()) {
System.out.println( ste.getMethodName() );
}
}
这里使用getMethodName()方法来给出异常栈轨迹所经过的方法名!
重抛异常
try {
...
} catch( Exception e ) {
throw e; // 重新抛出一个异常!
}
若只是简单地将异常重新抛出,则而后用printStackTrace()显示的将是原异常抛出点的调用栈信息,而非重新抛出点的信息,欲更正该信息,可以使用fillInStackTrace()方法:
try {
...
} catch( Exception e ) {
throw (Exception)e.fillInStackTrace(); // 该行就成了异常的新发生地!
}
异常链
异常链:在捕获一个异常后抛出另一个异常,并希望将原始的异常信息保存下来!
解决办法:
1. 在异常的ctor中加入cause参数
2. 使用initCause()方法
注意:Throwable子类中,仅三种基本的异常类提供了待cause参数的ctor(Error、Exception、RuntimeException),其余情况只能靠initCause()方法。
Java标准异常
看这个图需要明确:程序员一般关心Exception基类型的异常
- 由图中可知,Error、RuntimeException都叫做“Unchecked Exception”,即不检查异常,程序员也无需写异常处理的代码,这种自动捕获
- 若诸如RuntimeException这种Unchecked异常没有被捕获而直达main(),则程序在退出前将自动调用异常的printStackTrace()方法
使用finally进行清理
try {
...
} catch(...) {
...
} finally { // finally子句总是会被执行!!!
...
}
使用时机:
- 当需要把内存之外的资源(如:文件句柄、网络连接、某个外部世界的开关)恢复到初始状态时!
try {
...
} catch(...) {
...
} finally { // finally子句总是会被执行!!!
sw.off(); // 最后总是需要关掉某个开关!
}
- 在return中使用finally
public static void func( int i ) {
try {
if( i==1 )
return;
if( i==2 )
return;
} finally {
print( "Performing cleanup!" ); // 即使上面有很多return,但该句肯定被执行
}
}
finally存在的缺憾:两种情况下的finally使用会导致异常丢失!
- 前一个异常还未处理就抛出下一个异常
// 异常类
class VeryImportantException extends Exception {
poublic String toString() {
return "A verfy important exception!";
}
}
class HoHumException extends Exception {
public String toString() {
return "A trivial exception!";
}
}
------------------------------------------------------------------
// 使用异常的客户端
public class LostMessage {
void f() throws VeryImportantException {
throw new VeryImportantException();
}
void dispose() throws HoHumException {
throw new HoHumException();
}
public static void main( String[] args ) {
try {
LostMessage lm = new LostMessage();
try {
lm.f();
} finally {
lm.dispose(); // 最后只会该异常生效,lm.f()抛出的异常丢了!
}
} catch( Exception e ) {
System.out.println(e);
}
}
}
-----------------------------------------------------------------
// 输出
A trivial exception!
- finally子句中的return
public static void main( String[] args ) {
try {
throw new RuntimeException();
} finally {
return; // 这将会掩盖所有的异常抛出
}
}