从制造到创造
软件工程师成长之路
posts - 249,  comments - 96,  trackbacks - 0

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(
10);
        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 阅读(776) 评论(0)  编辑  收藏 所属分类: Java-14.Exception

只有注册用户登录后才能发表评论。


网站导航:
 

<2008年2月>
272829303112
3456789
10111213141516
17181920212223
2425262728291
2345678

常用链接

留言簿(9)

我参与的团队

随笔分类(245)

随笔档案(239)

文章分类(3)

文章档案(3)

收藏夹(576)

友情链接

搜索

  •  

积分与排名

  • 积分 - 430042
  • 排名 - 118

最新评论

阅读排行榜

评论排行榜