“Java的基本理念是:结构不佳的代码不能运行。”,从我原来使用Java的异常机制的感觉是不太喜欢的,因为他将整个异常的调用轨迹都放出来了,给人一种恐怖的感觉,现在慢慢体会着这里面的种种好处,感受着他们的设计思路。
1.开发异常的初衷
在Java中,异常机制的使用的初衷是为了方便程序员处理可能的错误,其中一个重要原则是“只有在你知道如何处理的情况下才捕获异常”,同时也是将错误处理代码与正常运行逻辑的代码分开,增强可阅读与可维护性。如果常使用c语言的话,常常函数的调用是通过返回值来进行可能存在错误的判断,一旦情况复杂,直接干扰了正常运行逻辑的理解,看到的全是对可能问题的处理,因此,往往很少充分的去处理(这样确实简单,可以通过调试跟踪来解决,这个可能就是对问题的处理方式不同吧)。书中阐述了些“被检查的异常”和强静态类型检查对开发健壮程序的看法,强调异常报告和类型检查的重要性,但是也不限制死;自己在C语言中深感其灵活性(也是经常被弄得混乱无助),但在Java中还没有特别切身的感受,不好多说,留下来。
2.异常的抛出与捕获
(1)异常的产生
java.lang.Throwable是 Java 语言中所有错误或异常的基类。其直接子类为Error和Exception,Error用来表示系统的错误,一般应用程序不需要关心;Exception需要应用程序关心,是类库、用户方法及运行时可抛出的基本类型。只有当对象是Throwable(或其子类之一)的实例时,才能通过 Java 虚拟机或者 Java throw 语句抛出。类似地,只有此类或其子类之一才可以是 catch 子句中的参数类型。因此通常情况下,可以通过catch Exception来捕获想要的(关心的)所有异常,如果没有处理好同时也带来“吞食”异常的问题。
(2)抛出与捕获(异常栈轨迹)
可以通过继承Exception或者更为细的异常类型来创建自己所需要的异常,然后使用throw来抛出异常,遵循Java的对象使用,异常对象也是通过new在堆上创建,而且可以通过无参数的和带字符串为参数的构造器来构造异常对象,例如中的throw new NullPointerException("t = null");或者还能够通过cause来构造异常链。
异常的捕获是将可能产生异常的语句放在try块中,然后在catch中处理异常;要说的是,可以通过printStackTrace()方法显示异常的调用栈轨迹,可以在捕获处来显示异常的调用轨迹,但是这里也涉及到如果未处理好异常,异常重新抛出后,栈轨迹的变化问题:如果想保留原来的调用栈轨迹,就直接抛出异常;而如果想更新这个信息,可以调用fillInStackTrace()方法。
异常链,保存的所有异常的信息。Throwable的子类(Error、Exception、RuntimeException)在构造器中可以接受一个cause对象作为参数,这个cause可以作为新异常产生的前因,从而记录所有异常信息。
(3)finally
使用finally清理,无论try块中的异常是否抛出,它们都能得到执行,即使finally所连接的try方法中有return语句,也会保证finally的执行;虽然不能用作清理内存析构函数之类,但是可以在打开的文件或网络连接等地方使用。
3.异常的限制
当覆盖方法时,只能抛出在基类方法的异常说明里列出的异常,从而使得基类使用的代码(方法)能够用到其子类上;当然,这个限制对构造器是不起作用的,但是子类的构造器的异常说明必须包含基类的异常说明,而且子类的构造器是不能捕获基类构造器抛出的异常。
最后一个异常的使用例子:通过printStackTrace()来进行异常栈轨迹的查看,而通过getCause()获取产生此异常发生的直接原因(异常),因而得到整个异常链。
package com.test.myjava;
/*
* @author w7849516230
*/
class FirstException extends Exception{
/*
* 利用默认的构造器
* Exception(String message, Throwable cause) 构造带指定详细消息和原因的新异常
* 下同
*/
FirstException(String str,Exception e){
super(str,e);
}
}
class SecondException extends Exception{
public SecondException(String str, Exception e) {
super(str,e);
}
}
class ThirdException extends Exception{
public ThirdException(String str) {
super(str);
}
}
public class ExceptionTest {
public void firstMethod() throws FirstException{
try {
secondMethod();
} catch (SecondException e) {
throw new FirstException("FirstException Test",e);
}
}
public void secondMethod() throws SecondException{
try {
thirdMethod();
} catch (ThirdException e) {
throw new SecondException("SecondException Test",e);
}
}
public void thirdMethod() throws ThirdException{
throw new ThirdException("ThirdException Test");
}
public static void main(String[] args){
ExceptionTest et = new ExceptionTest() ;
//打印异常栈
try {
et.firstMethod();
} catch (FirstException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
et.firstMethod();
} catch (Exception e) {
try {
throw e.getCause();//捕获特定的异常进行处理
}catch(FirstException ef){
System.out.println("Catch FirstException!");
}catch(SecondException es){
System.out.println("Catch SecondException!");
try {
throw es.getCause();
}catch(ThirdException eth){
System.out.println("In SecondException Catch ThirdException!");
}catch (Throwable e1) {
// TODO Auto-generated catch block
System.out.println("Catch Exception in SecondException");
e1.printStackTrace();
}
}catch(ThirdException eth){
System.out.println("Catch ThirdException");
}
catch (Throwable e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
}
}
输出:
com.test.myjava.FirstException: FirstException Test
at com.test.myjava.ExceptionTest.firstMethod(ExceptionTest.java:34)
at com.test.myjava.ExceptionTest.main(ExceptionTest.java:53)
Caused by: com.test.myjava.SecondException: SecondException Test
at com.test.myjava.ExceptionTest.secondMethod(ExceptionTest.java:41)
at com.test.myjava.ExceptionTest.firstMethod(ExceptionTest.java:32)
... 1 more
Caused by: com.test.myjava.ThirdException: ThirdException Test
at com.test.myjava.ExceptionTest.thirdMethod(ExceptionTest.java:45)
at com.test.myjava.ExceptionTest.secondMethod(ExceptionTest.java:39)
... 2 more
Catch SecondException!
In SecondException Catch ThirdException!