15、异常
使用异常的好处:一是使用程序更加的健壮。二是它往往能够降低错误处理的复杂度。如果不使用异常,那么就必须检查特定的错误,并在程序中的许多地方去处理它,而如果使用异常,那就不必在方法调用处进行检查,因为异常机制保证能够捕获这个错误,并且,只需在一个地方处理错误,即所谓的异常处理程序块中。这种方式不仅节省代码,而且把“描述在正常执行过程中做什么事”的代码和“出了问题怎么办”的代码相分离,总之,与以前的错误处理方法相比,异常机制使用代码的阅读、编写和调试工作更加高效。
?
当抛出异常后,有几件事会随之发生。首先,同Java中其他对象的创建一样,将使用new在堆上创建异常对象。然后,当前的执行路径(它不能继续下去了)被终止,并且从当前环境中弹出对异常对象的引用。此时,异常处理机制接管程序,并开始寻找一个恰当的地方来继续执行程序。这个恰当的地方就是异常处理程序,它的任务是将程序从错误状态中恢复,以使用程序能要么换一种方式运行,要么继续运行下去。
?
通常把错误信息输出到e.printStackTrace(System.out)要比直接e.printStackTrace()要好,因为System.out也许会重定向,而e.printStackTrace()默认则是将信息输出到操作系统标准错误流,所以一般我们使用e.printStackTrace(System.out)打印异常信息。如果把结果送到System.err,它就会把会随System.out一起被重定向,这样更容易被用户注意。
?
使用程序包的客户端程序员可能仅仅只是查看一下抛出的异常类型,其他的就不管了(大多数Java库里的异常都是这么用的),所以对异常所添加的其他功能也许根本用不上,名称代表发生的问题,并且异常的名称应该可以望文知意。
?
可以声明方法将抛出异常,实际上却不抛出,这样做的好处是,为异常先占个位子,以后就可以抛出这种异常而不用修改已有的代码。在定义抽象基类和接口时这种能力很重要,这样派生类或接口实现就能够抛出这些预先声明的异常。
Exception的方法:
getMessage():My Exception
getLocalizedMessage():My Exception
toString():java.lang.Exception: My Exception
printStackTrace():
java.lang.Exception: My Exception
?at excep.ExceptionMethods.main(ExceptionMethods.java:10)?
?
使用JDK日志器记录日志
2010-2-9 11:17:38 excep.LoggingExceptions logException
严重: excep.MyException: errKey = 100 null
?at excep.LoggingExceptions.main(LoggingExceptions.java:37)?
?
printStackTrace()方法所提供的信息可以通过getStackTrace()方法来直接访问,这个方法返回一个由栈轨迹中的元素所构成的数组,其中每一个元素都表示栈中的一桢。元素0是栈顶元素,并且是调用序列中的最后一个方法调用。数组中的最后一个元素即栈底是调用序列中的第一个方法调用。
getClassName: WhoCalled getFileName: WhoCalled.java getLineNumber: 8 getMethodName: f isNativeMethod: false
getClassName: WhoCalled getFileName: WhoCalled.java getLineNumber: 28 getMethodName: main isNativeMethod: false
--------------------------------
getClassName: WhoCalled getFileName: WhoCalled.java getLineNumber: 8 getMethodName: f isNativeMethod: false
getClassName: WhoCalled getFileName: WhoCalled.java getLineNumber: 20 getMethodName: g isNativeMethod: false
getClassName: WhoCalled getFileName: WhoCalled.java getLineNumber: 30 getMethodName: main isNativeMethod: false
--------------------------------
getClassName: WhoCalled getFileName: WhoCalled.java getLineNumber: 8 getMethodName: f isNativeMethod: false
getClassName: WhoCalled getFileName: WhoCalled.java getLineNumber: 20 getMethodName: g isNativeMethod: false
getClassName: WhoCalled getFileName: WhoCalled.java getLineNumber: 24 getMethodName: h isNativeMethod: false
getClassName: WhoCalled getFileName: WhoCalled.java getLineNumber: 32 getMethodName: main isNativeMethod: false
如果只是把当前异常对象重新抛出,那么printStackTrace()方法显示的将是原来异常抛出点的调用栈信息,而并非重新抛出点的信息。要想更新这个信息,可以调用fillInStackTrace()方法,这将返回一个Throwable对象,它是通过把当前调用栈信息填入原来那个异常对象而建立的:?
异常处理的一个重要的原则是“只有在你知道如何处理的情况下才捕获异常”。实际上,异常处理的一个重要目标就是把错误处理的代码同错误发生的地点相分离。这使你能在一段代码中专注于要完成的事情,至于如何处理错误,则放在另一段代码中。这样以来,主干代码就不会与错误处理逻辑混在一起,也更容易理解和维护。?
“被检查的异常”可能使问题变得复杂,因为它们强制你在可能还没有准备好处理错误的时候被迫加上cacth子名,即使我们不知道如何处理的情况下,这就导致了异常的隐藏:
public class Test {static void f() {try {throw new Exception();} catch (Exception e) {//将检测性异常转换成非检测性异常后继续抛出throw new RuntimeException(e);}}static void g() {//不用捕获,因为检测异常转换了运行异常f();}static void h() throws Exception {try {g();} catch (Exception e) {e.printStackTrace();System.out.println("-----");//可以将检测异常继续做为检测异常抛出throw new Exception(e.getCause());}}public static void main(String[] args) {try {h();} catch (Exception e) {e.printStackTrace();}}}java.lang.RuntimeException: java.lang.Exception
??????? at Test.f(Test.java:7)
??????? at Test.g(Test.java:13)
??????? at Test.h(Test.java:18)
??????? at Test.main(Test.java:29)
Caused by: java.lang.Exception
??????? at Test.f(Test.java:4)
??????? ... 3 more
-----
java.lang.Exception: java.lang.Exception
??????? at Test.h(Test.java:23)
??????? at Test.main(Test.java:29)
Caused by: java.lang.Exception
??????? at Test.f(Test.java:4)
??????? at Test.g(Test.java:13)
??????? at Test.h(Test.java:18)
??????? ... 1 more