`

第12章 异常处理

 
阅读更多
1.Throwable类是所有异常类的基类,Throwable类下面有两个基本类型Error用来表示系统错误,通常用户不用关心,Exception类是运行时抛出异常的基类,是通常用户关心的异常类的基类。

2.Exception类下面又分为RuntimeExcepton和非运行时异常。运行异常都是RuntimeException类及其子类异常,如NullPointerException、IndexOutOfBoundException等,运行时异常是不检查异常。在程序中可以捕获也可以不捕获。
非运行时异常是RuntimeException以外的异常,在程序中必须被处理,如果不处理,程序就编译不能通过。如IOException、SQLException等用户自定义的Exception异常。

非运行时异常也叫被检查异常,是编译期间内强制检查的类型。


3.当异常抛出后,
首先,同java其他对象的创建一样,将使用new在堆上创建异常对象
然后,当前执行路径被终止,并且从当前环境中弹出对异常对象的引用;
最后,异常处理机制接管程序,并开始寻找一个恰当的地方来继续执行程序。这个恰当的地方就是异常处理程序,它的任务是将程序从错误状态中恢复,以使程序能换一种方式运行等。

4.try块也叫“监控区域”,它是一段可能产生异常的代码

5.异常处理程序
异常处理程序紧跟在try块后面,以关键字catch表示。当异常被抛出时,异常处理机制负责在异常处理程序中寻找第一个参数与异常对象相匹配的catch块,然后进入执行,此时异常就得到了处理。注意:和case不一样,只有匹配的catch才能得到执行。

6异常处理理论上有两种基本类型:终止模型和恢复模型
但是,一般任何只使用终止模型

7.创建自定义异常
要创建自定义异常,必须从已有的异常类继承,最好选择意思相近的异常类继承。
创建自定义异常类最简单的方法就是让编译器为你产生默认的构造器,例如:
class SimpleException extends Exception {
}

public class InheritingExceptions {
    public void f() throws SimpleException {
        System.out.println("Throw SimpleException from f()");
        throw new SimpleException();
    }

    public static void main(String[] args) {
        InheritingExceptions sed = new InheritingExceptions();
        try {
            sed.f();
        } catch (SimpleException e) {
            System.out.println("Caught it!");
        }
    }
}
还可以创建带参数的构造器:
class MyException extends Exception {
  public MyException() {}
  public MyException(String msg) { super(msg); }
}

public class FullConstructors {
  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("Originated 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);
    }
  }
}

7
printStackTrace方法,将输出,从方法调用处到异常抛出处的方法调用序列,(顺序是从下到上)

8.异常说明
如果方法产生异常,要么处理异常;要么在方法声明处说明该异常即异常说明。
异常说明的关键字是throws,例如:
void f() throws Toobig,TooSmall{}
另外一种情况是,声明异常,但实际上并不抛出该异常。好处时,先为异常占一个位置,以后抛出该异常,就不用修改已有的代码。

9.异常对象的这个方法:getStackTrace()会返回一个数组形式的调用栈,第一个元素是异常抛出处,剩下的是方法调用链,例如:
public class WhoCalled {
  static void f() {
    // Generate an exception to fill in the stack trace
    try {
      throw new Exception();
    } catch (Exception e) {
      for(StackTraceElement ste : e.getStackTrace())
        System.out.println(ste.getMethodName());
    }
  }
  static void g() { f(); }
  static void h() { g(); }
  public static void main(String[] args) {
    f();
    System.out.println("--------------------------------");
    g();
    System.out.println("--------------------------------");
    h();
  }
}

10.重新抛出异常
有时希望把刚捕获的异常重新抛出,尤其是在使用Exception捕获所有异常的时候。

重抛异常,会把异常抛给上一级环境中得异常处理程序,重新抛出异常所在地方的异常处理程序将被忽略。此外,异常对象的所有信息都得以保存。

如果只是把当前异常对象重新抛出,那么原来异常抛出点得信息将会保存,而并非重新抛出点得信息。要想更新这个信息,可以调用fillInStackTrace方法,它是通过把当前调用栈信息填入原来那个异常对象而建立的。例如:
public class Rethrowing {
  public static void f() throws Exception {
    System.out.println("originating the exception in f()");
    throw new Exception("thrown from f()");
  }
  public static void g() throws Exception {
    try {
      f();
    } catch(Exception e) {
      System.out.println("Inside g(),e.printStackTrace()");
      e.printStackTrace(System.out);
      throw e;
    }
  }
  public static void h() throws Exception {
    try {
      f();
    } catch(Exception e) {
      System.out.println("Inside h(),e.printStackTrace()");
      e.printStackTrace(System.out);
      throw (Exception)e.fillInStackTrace();
    }
  }
  public static void main(String[] args) {
    try {
      g();
    } catch(Exception e) {
      System.out.println("main: printStackTrace()");
      e.printStackTrace(System.out);
    }
    try {
      h();
    } catch(Exception e) {
      System.out.println("main: printStackTrace()");
      e.printStackTrace(System.out);
    }
  }
}


11.无论try块是否抛出异常,finally块总会得到执行;

12.try如果抛出异常,通过while循环还可以重新运行抛出异常的代码,如下:
class ThreeException extends Exception {}

public class FinallyWorks {
  static int count = 0;
  public static void main(String[] args) {
    while(true) {
      try {
        // Post-increment is zero first time:
        if(count++ == 0)
          throw new ThreeException();
        System.out.println("No exception");
      } catch(ThreeException e) {
        System.out.println("ThreeException");
      } finally {
        System.out.println("In finally clause");
        if(count == 5) break; // out of "while"
      }
    }
  }
}

注意:finally块中可以使用break;退出循环。

13.在一个既有try又有finally的程序中,技术在try块中有return语句返回,那么在返回之前,finally块也会被执行 ,例如:
public class MultipleReturns {
  public static void f(int i) {
  System.out.println("Initialization that requires cleanup");
    try {
      System.out.println("Point 1");
      if(i == 1) return;
      System.out.println("Point 2");
      if(i == 2) return;
      System.out.println("Point 3");
      if(i == 3) return;
      System.out.println("End");
      return;
    } finally {
    System.out.println("Performing cleanup");
    }
  }
  public static void main(String[] args) {
    for(int i = 1; i <= 4; i++)
      f(i);
  }
}

14.
try 块后可同时接 catch 和 finally 块,但至少要它们两者中的一个。
必须在 try 之后添加 catch 或 finally 块。
必须遵循块顺序:若代码同时使用 catch 和 finally 块,则必须将 catch 块放在 try 块之后。

15.异常丢失
如果只有try和finally块,那么如果try块抛出了异常A,finally块也抛出了异常B,那么异常A就会被异常B取代,异

常A的信息就丢失了,如下:
class VeryImportantException extends Exception {
  public String toString() {
    return "A very 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();
      }
    } catch(Exception e) {
      System.out.println(e);
    }
  }
}

16.一种更加简单的丢失异常的方法是在,finally块中使用return,这样会忽略所有在try块中抛出的异常,如下所示 :
public class ExceptionSilencer {
  public static void main(String[] args) {
    try {
      throw new RuntimeException();
    } finally {
      // Using 'return' inside the finally block
      // will silence any thrown exception.
      return;
    }
  }
}

注:方法遇到return就结束了,即使有异常也不会再处理了,所以可以忽略所有异常

17
如果基类的构造器有异常说明列表,那么导出类的构造函数也必须要有相应的异常说明列表(因为子类有可能自动调用基类的构造器),而且还可以添加新的异常,见下面的类:

class BaseballException extends Exception {}
class Foul extends BaseballException {}
class Strike extends BaseballException {}

abstract class Inning {
  public Inning(){}
  public Inning(String s) throws BaseballException {}
}

public class StormyInning extends Inning  {
  public StormyInning()
    throws RainedOut, BaseballException {}
  public StormyInning(String s){}
}
可以看出:子类构造器异常声明列表取决于它要调用哪个基类构造器,调用哪个,就必须声明那个的异常列表;

还有,派生类构造器不能捕获基类构造器的异常。

18.
在继承基类或是实现接口时,如果原来方法没有异常声明列表,那么在覆盖或是实现该方法时,也不能有异常说明列表;

19
在继承基类或是实现接口时,如果原来方法有异常声明列表,那么在覆盖或是实现该方法时,只能说明基类或接口中方法的异常说明列表,不能添加其他的异常,不过可以没有异常声明,也可以比原来的异常声明少,也可以是基类或接口中异常类的子类;
注意:但基类和接口有同名的方法,且有不同的异常声明,那么子类在既继承又实现时,该方法无法处理,编译不通过;除非这个方法在子类中,什么异常也不声明。

20
如果正在处理当前类的对象,编译器只会强制要求你捕获这个类所抛出的异常,或者是只捕获所有异常类的基类。

下面的例子包含了所有的情况



//: exceptions/StormyInning.java
// Overridden methods may throw only the exceptions
// specified in their base-class versions, or exceptions
// derived from the base-class exceptions.

class BaseballException extends Exception {}
class Foul extends BaseballException {}
class Strike extends BaseballException {}

abstract class Inning {
  public Inning() throws BaseballException {}
  public void event() throws BaseballException {
    // Doesn't actually have to throw anything
  }
  public abstract void atBat() throws Strike, Foul;
  public void walk() {} // Throws no checked exceptions
}

class StormException extends Exception {}
class RainedOut extends StormException {}
class PopFoul extends Foul {}

interface Storm {
  public void event() throws RainedOut;
  public void rainHard() throws RainedOut;
}

public class StormyInning extends Inning implements Storm {
  // OK to add new exceptions for constructors, but you
  // must deal with the base constructor exceptions:
  public StormyInning()
    throws RainedOut, BaseballException {}
  public StormyInning(String s)
    throws Foul, BaseballException {}
  // Regular methods must conform to base class:
//! void walk() throws PopFoul {} //Compile error
  // Interface CANNOT add exceptions to existing
  // methods from the base class:
//! public void event() throws RainedOut {}
  // If the method doesn't already exist in the
  // base class, the exception is OK:
  public void rainHard() throws RainedOut {}
  // You can choose to not throw any exceptions,
  // even if the base version does:
  public void event() {}
  // Overridden methods can throw inherited exceptions:
  public void atBat() throws PopFoul {}
  public static void main(String[] args) {
    try {
      StormyInning si = new StormyInning();
      si.atBat();
    } catch(PopFoul e) {
      System.out.println("Pop foul");
    } catch(RainedOut e) {
      System.out.println("Rained out");
    } catch(BaseballException e) {
      System.out.println("Generic baseball exception");
    }
    // Strike not thrown in derived version.
    try {
      // What happens if you upcast?
      Inning i = new StormyInning();
      i.atBat();
      // You must catch the exceptions from the
      // base-class version of the method:
    } catch(Strike e) {
      System.out.println("Strike");
    } catch(Foul e) {
      System.out.println("Foul");
    } catch(RainedOut e) {
      System.out.println("Rained out");
    } catch(BaseballException e) {
      System.out.println("Generic baseball exception");
    }
  }
} ///:~
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics