常用 API
异常的分类
异常对象都是派生于 Throwable 类的一个实例。如果 Java 中内置的异常类不能够满足需求,用户可以创建自己的异常类。
对于那些可能被他人使用的 Java 方法,应该根据异常规范,在方法的首部末尾来声明这个方法可能抛出的异常,如果可能有多个异常,那么异常名通过逗号分隔。
一个方法必须声明所有可能抛出的受查异常,而非受查异常要么不可控制( Error ),要么就应该避免发生( RuntimeException )。如果方法没有声明所有可能发生的受查异常,编译器就会发出一个错误消息。
除了声明异常之外,还可以捕获异常。这样会使异常不被抛到方法之外,也不需要 throws 规范。
Error
Error 类层次结构描述了 Java 运行时系统的内部错误和资源耗尽错误。 应用程序不应该抛出这种类型的对象。 如果出现了这样的内部错误,除了通告给用户,并尽力使程序安全地终止之外,再也无能为力了。这种情况很少出现。
Exception
Exception 又分解为两个分支: 一个分支派生 RuntimeException;另一个分支包含其他异常。划分两个分支的规则是:由程序错误导致的异常属于 RuntimeException ; 而程序本身没有问题,但由于像 I/O错误这类问题导致的异常属于其他异常。
RuntimeException 和 Error 是不会受到编译器检查的,叫作非检查异常。如果出现 RuntimeException 异常, 那么就一定是你的问题,属于Bug。
其他的异常类型是受检查异常,编译器将核查是否为所有的受査异常提供了异常处理器。
手动抛出异常
一旦方法抛出了异常,这个方法就不可能返回到调用者。也就是说,不必为返回的默认值或错误代码担忧。
手动抛出异常使用:throw new EOFException( );
自定义异常
定义一个派生于Exception 的类, 或者派生于 Exception 子类的类。
捕获异常
捕获到异常后,打印详细信息可以使用:exception.printStackTrace( );
异常对象可能包含与异常本身有关的信息。可以使用 e.getMessage( )
获得对象的更多信息,使用e.getClass().getName( )
得到异常对象的实际类型。
try catch 语句
如果在 try 语句块中的任何代码抛出了一个在 catch 子句中说明的异常类,那么程序将跳过 try 语句块的其余代码,并且开始执行 catch 子句中的处理器代码。
如果在 try 语句块中的代码没有拋出任何异常,那么程序将跳过 catch 子句。
如果方法中的任何代码拋出了一个在 catch 子句中没有声明的异常类型,那么这个方法就会立刻退出。
捕获多个异常
捕获后再次抛出
在 catch 子句中可以抛出一个异常,这样做的目的是改变异常的类型。捕获到异常后进行包装或者修改后重新抛出。方便后续逻辑处理,注意一定要使用initCause方法将原始的异常信息保留下来方便日志定位。
强烈建议使用这种包装技术。这样可以让用户抛出子系统中的高级异常,而不会丢失原始异常的细节。
finally子句
不管是否有异常被捕获,finally 子句中的代码都被执行。通常用来关闭资源。try 语句可以只有 finally 子句, 而没有 catch 子句。 强烈建议解耦合 try/catch和 try/finally语句块。
内层的 try 语句块只有一个职责,就是确保关闭输入流。外层的 try 语句块也只有一个职责,就是确保报告出现的错误。这种设计方式不仅清楚,而且还具有一个功能,就是将会报告 finally 子句中出现的错误。
另外如果 finally 子句中也有一个 return 语句,这个返回值将会覆盖原始的返回值。
带资源的 try 语句
这个块正常退出时,或者存在一个异常时,都会调用 in.close( )
方法,就好像使用了 finally 块一样。而且 try( )
的内容可以指定多个资源,用;
分隔。
分析堆栈轨迹
可以调用 Throwable 类的 printStackTrace 方法访问堆栈轨迹的文本描述信息。
一种更灵活的方法是使用 getStackTrace 方法, 它会得到 StackTraceElement 对象的一个数组, 可以在你的程序中分析这个对象数组。StackTraceElement 类含有能够获得文件名和当前执行的代码行号的方法,同时,还含有能够获得类名和方法名的方法。 toString 方法将产生一个格式化的字符串, 其中包含所获得的信息。
异常使用技巧
- 只在异常情况下使用异常机制
- 捕获时不要过分地细化异常
- 利用异常层次结构,找到合适的异常
- 在检测错误时,苛刻要比放任更好
- 早抛出,晚捕获
断言
断言机制允许在测试期间向代码中插入一些检査语句。当代码发布时,这些插人的检测语句将会被自动地移走。
可以使用下面两种格式:
assert 表达式;
assert 表达式: 信息;
这两种形式都会对条件进行检测,如果结果为 false,则抛出一个 AssertionError 异常。在第二种形式中,表达式将被传人 AssertionError 的构造器,并转换成一个消息字符串。
在默认情况下,断言被禁用。可以在运行程序时用 -enableassertions
或 -ea
选项启用。
断言只应该用于在测试阶段确定程序内部的错误位置。断言是一种测试和调试阶段所使用的战术性工具,而日志记录是一种在程序的整个生命周期都可以使用的策略性工具。
笔记大部分摘录自《Java核心技术卷I》,含有少数本人修改补充痕迹。