9.1 Java异常处理机制概述
主要考虑的两个问题:(1)如何表示异常情况?(2)如何控制处理异常的流程?
9.1.1 Java异常处理机制的优点
Java语言按照面向对象的思想来处理异常,使得程序具有更好的可维护性。
Java异常处理机制具有以下优点:
- 把各种不同类型的异常情况进行分类,用Java类来表示异常情况,发挥类的可扩展性和可重用性。
- 异常流程的代码和正常流程的代码分离,提供了程序的可读性,简化了程序的结构。
- 可以灵活地处理异常,如果当前方法有能力处理异常,就捕获并处理它,否则只需要抛出异常,由方法调用者来处理它。
9.1.2 Java虚拟机的方法调用栈
如果方法中的代码块可能抛出异常,有如下两种处理方法:
(1)在当前方法中通过try...catch语句捕获并处理异常;
(2)在方法的声明处通过throws语句声明抛出异常。
当Java虚拟机追溯到调用栈的底部的方法时,如果仍然没有找到处理该异常的代码,将按以下步骤处理:
(1)调用异常对象的printStachTrace()方法,打印来自方法调用栈的异常信息。
(2)如果该线程不是主线程,那么终止这个线程,其它线程继续正常运行。如果该线程是主线程,那么整个应用程序被终止。
9.1.3 异常处理对性能的影响
一般来说,影响很小,除非方法嵌套调用很深。
9.2 运用Java异常处理机制
9.2.1 try...catch语句:捕获异常
9.2.2 finally语句:任何情况下都必须执行的代码
主要用于关闭某些流和数据库连接。
9.2.3 thorws子句:声明可能会出现的异常
9.2.4 throw语句:抛出异常
9.2.5 异常处理语句的语法规则
(1)try代码块不能脱离catch代码块或finally代码块而单独存在。try代码块后面至少有一个catch代码块或finally代码块。
(2)try代码块后面可以有零个或多个catch代码块,还可以有零个或至多一个finally代码块。
(3)try代码块后面可以只跟finally代码块。
(4)在try代码块中定义的变量的作用域为try代码块,在catch代码块和finally代码块中不能访问该变量。
(5)当try代码块后面有多个catch代码块时,Java虚拟机会把实际抛出的异常类对象依次和各个catch代码块声明的异常类型匹配,如果异常对象为某个异常类型或其子类的实例,就执行这个catch代码块,而不会再执行其他的catch代码块。
(6)如果一个方法可能出现受检查异常,要么用try...catch语句捕获,要么用throws子句声明将它抛出,否则会导致编译错误。
9.2.6 异常流程的运行过程
(1)finally语句不被执行的唯一情况是先执行了用于终止程序的System.exit()方法。
(2)return语句用于退出本方法。
(3)finally代码块虽然在return语句之前被执行,但finally代码块不能通过重新给变量赋值来改变return语句的返回值。
(4)建议不要在finally代码块中使用return语句,因为它会导致以下两种潜在的错误
A:覆盖try或catch代码块的return语句
public class SpecialException extends Exception {
public SpecialException() {
}
public SpecialException(String msg) {
super(msg);
}
}
public class FinallyReturn {
/**
* @param args
*/
public static void main(String[] args) {
FinallyReturn fr = new FinallyReturn();
System.out.println(fr.methodB(1));// 打印100
System.out.println(fr.methodB(2));// 打印100
}
public int methodA(int money) throws SpecialException {
if (--money <= 0) {
throw new SpecialException("Out of money");
}
return money;
}
@SuppressWarnings("finally")
public int methodB(int money) {
try {
return methodA(money);// 可能抛出异常
} catch (SpecialException e) {
return -100;
} finally {
return 100;// 会覆盖try和catch代码块的return语句
}
}
}
B:丢失异常
public class ExLoss {
/**
* @param args
*/
public static void main(String[] args) {
try {
System.out.println(new ExLoss().methodB(1));// 打印100
System.out.println("No Exception");
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
public int methodA(int money) throws SpecialException {
if (--money <= 0) {
throw new SpecialException("Out of money");
}
return money;
}
@SuppressWarnings("finally")
public int methodB(int money) {
try {
return methodA(money);// 可能抛出异常
} catch (SpecialException e) {
throw new Exception("Wrong");
} finally {
return 100;// 会丢失catch代码块中的异常
}
}
}
9.3 Java异常类
所有异常类的祖先类为java.lang.Throwable类,它的实例表示具体的异常对象,可以通过throw语句抛出。
Throwable类提供了访问异常信息的一些方法,常用的方法包括:
- getMessage() --返回String类型的异常信息。
- printStachTrace()--打印跟踪方法调用栈而获得的详细异常信息。在程序调试阶段,此方法可用于跟踪错误。
public class ExTrace {
/**
* @param args
*/
public static void main(String[] args) {
try {
new ExTrace().methodB(1);
} catch (Exception e) {
System.out.println("--- Output of main() ---");
e.printStackTrace();
}
}
public void methodA(int money) throws SpecialException {
if (--money <= 0) {
throw new SpecialException("Out of money");
}
}
public void methodB(int money) throws Exception {
try {
methodA(money);
} catch (SpecialException e) {
System.out.println("--- Output of methodB() ---");
System.out.println(e.getMessage());
throw new Exception("Wrong");
}
}
}
打印结果:
--- Output of methodB() ---
Out of money
--- Output of main() ---
java.lang.Exception: Wrong
at chapter09.d0903.ExTrace.methodB(ExTrace.java:45)
at chapter09.d0903.ExTrace.main(ExTrace.java:26)
Throwable类有两个直接子类:
- Error类--表示仅靠程序本身无法恢复的严重错误,如内存不足等。
- Exception类--表示程序本身可以处理的异常。
9.3.1 运行时异常
RuntimeException类及其子类都被称为运行时异常,这种异常的特点是Java编译器不会检查它,会编译通过,但运行时如果条件成立就会出现异常。
例如当以下divided()方法的参数b为0,执行“a/b”操作时会出现ArrithmeticException异常,它属于运行时异常,Java编译器不会检查它。
public int divide2(int a, int b) {
return a / b;// 当参数为0,抛出ArrithmeticException
}
下面的程序中的IllegalArgumentException也是运行时异常,divided()方法即没有捕获它,也没有声明抛出它。
public class WithRuntimeEx {
/**
* @param args
*/
public static void main(String[] args) {
new WithRuntimeEx().divide(1, 0);
System.out.println("End");
}
public int divide(int a, int b) {
if (b == 0) {
throw new IllegalArgumentException("除數不能為0");
}
return a / b;
}
}
由于程序代码不会处理运行时异常,因此当程序在运行时出现了这种异常时,就会导致程序异常终止。以上程序的打印结果为:
Exception in thread "main" java.lang.IllegalArgumentException: 除數不能為0
at chapter09.d0903.WithRuntimeEx.divide(WithRuntimeEx.java:29)
at chapter09.d0903.WithRuntimeEx.main(WithRuntimeEx.java:23)
9.3.2 受检查异常
除了RuntimeException及其子类以外,其他的Exception类及其子类都属于受检查异常(Checked Exception)。这种异常要么catch语句捕获,要么throws子句声明抛出,否则编译出错。
9.3.3 区分运行时异常和受检查异常
受检查异常表示程序可以处理的异常。
运行时异常表示无法让程序恢复运行的异常,导致这种异常的原因通常是由于执行了错误操作。一旦出现了错误操作,建议终止程序,因此Java编译器不检查这种异常。
9.3.4 区分运行时异常和错误
Error类及其子类表示程序本身无法修复的错误,它和运行时异常的相同之处是:Java编译器都不会检查它们,当程序运行时出现它们,都会终止程序。
两者的不同之处是:Error类及其子类表示的错误通常是由Java虚拟机抛出。
而RuntimeException类表示程序代码中的错误,它是可扩展的,用户可以根据特定的问题领域来创建相关的运行时异常类。
9.4 用户定义异常
9.4.1 异常转译和异常链
public class BaseException extends Exception {
protected Throwable cause = null;
public BaseException() {
}
public BaseException(String msg) {
super(msg);
}
public BaseException(Throwable cause) {
this.cause = cause;
}
public BaseException(String msg, Throwable cause) {
super(msg);
this.cause = cause;
}
public Throwable initCause(Throwable cause) {
this.cause = cause;
return this;
}
public Throwable getCause() {
return cause;
}
public void printStackTrace() {
printStackTrace(System.err);
}
public void printStackTrace(PrintStream outStream) {
printStackTrace(new PrintStream(outStream));
}
public void printStackTrace(PrintWriter writer) {
super.printStackTrace(writer);
if (getCause() != null) {
getCause().printStackTrace(writer);
}
writer.flush();
}
}
9.4.2 处理多样化异常
public class MultiBaseException extends Exception {
protected Throwable cause = null;
private List<Throwable> exceptions = new ArrayList<Throwable>();
public MultiBaseException() {
}
public MultiBaseException(Throwable cause) {
this.cause = cause;
}
public MultiBaseException(String msg, Throwable cause) {
super(msg);
this.cause = cause;
}
public List getException() {
return exceptions;
}
public void addException(MultiBaseException ex) {
exceptions.add(ex);
}
public Throwable initCause(Throwable cause) {
this.cause = cause;
return this;
}
public Throwable getCause() {
return cause;
}
public void printStackTrace() {
printStackTrace(System.err);
}
public void printStackTrace(PrintStream outStream) {
printStackTrace(new PrintStream(outStream));
}
public void printStackTrace(PrintWriter writer) {
super.printStackTrace(writer);
if (getCause() != null) {
getCause().printStackTrace(writer);
}
writer.flush();
}
}
9.5 异常处理原则
9.5.1 异常只能用于非正常情况
9.5.2 为异常提供说明文档
9.5.3 尽可能地避免异常
9.5.4 保持异常的原子性
9.5.5 避免过于庞大的try代码块
9.5.6 在catch子句中指定具体的异常类型
9.5.7 不要在catch代码块中忽略被捕获的异常,可以处理异常、重新抛出异常、进行异常转译
posted on 2008-02-22 17:50
CoderDream 阅读(880)
评论(0) 编辑 收藏 所属分类:
Java-14.Exception