Java中可以通过throw抛出所有继承Throwable的类。任何抛出的异常(RuntimeException除外)被编译器强制需要catch,如果不catch会出现编译错误。
- void makeExcetion()
- {
- // throw new MyException(); // error: this exception must be handled.
- }
如果需要把异常抛到方法外,则
需要在方法定义中声明该异常:
- void makeException() throws MyException
- {
- throw new MyException();
- }
这样编译器才会让你通过,但是
所有调用了这个方法的地方必须catch这个被声明的异常,否则编译不通过,(当然也可以通过上述声明异常的方式把异常抛到更外层)。相比之下,C++的异常声明更像一个提示,不带有强制性质。
Throwable分为两种类型:Error和Exception。Error用来表示编译时和系统错误,所以我们一般不用关心。Exception类有一个方法printStackTrace可以打印出异常抛出点的调用栈,另一个函数fillStackTrace可以在catch后更新调用栈信息再抛出。
RuntimeException是Exeption的子类,用来表示运行时发现的编程错误,比如访问空指针(NullPointerException)和数组越界(ArrayIndexOfBound***ception)。这种类型的异常是唯一不被编译器强制需要捕获的。如果一个RuntimeException抛出后没有被catch,程序会退出并调用printStackTrace方法。
使用finally块可以保证不管是否有异常发生,该块的代码都会被运行:
- try
- {
- // dosomething
- }
- catch (Exception e)
- {
- }
- finally
- {
- // always be executed. Usually dispose.
- }
finally也可以用来保证有多个return点的函数可以执行必要的清理工作,这也弥补了没有析构的不足。
有时候你可能需要把一些异常串成一个异常链。Exception.initCause可以支持这个需求:
- class LevelOneException extends Exception {}
- class LevelTwoException extends Exception {}
- class ExceptionMaker {
- void makeLevelOne() throws LevelOneException
- {
- throw new LevelOneException();
- }
- void makeLevelTwo() throws LevelTwoException
- {
- try {
- makeLevelOne();
- }
- catch (LevelOneException loe)
- {
- LevelTwoException lte = new LevelTwoException();
- lte.initCause(loe); // combine the exceptions
- throw lte;
- }
- }
- void Test()
- {
- try {
- makeLevelTwo();
- }
- catch (LevelTwoException lte)
- {
- Throwable t = lte.getCause(); // get the previous exception in the link
- }
- }
- }
在C++里,当一个异常没有被Catch的情况下,再抛出一个新的异常,比如析构函数里的异常,这个程序基本就over了。但在Java中,这种情况会导致旧的异常丢失,新的异常被传递出去:
- void loseException() {
- try {
- try {
- throw new LevelOneException();
- } finally {
- throw new LevelTwoException();
- }
- } catch (LevelTwoException lte) {
- lte.printStackTrace();
- }
- }
这应该算是Java的一个缺陷吧。
在覆写父类方法时,子类方法中的异常声明必须是父类声明的异常的子集。就是说子类的方法只能抛出比父类更少的异常,而不能抛出父类不会抛出的异常。
阅读(842) | 评论(0) | 转发(0) |