异常处理的任务就是将控制权从错误产生的地方转移给能够处理这种情况的错误处理器 。
7.1.1 异常分类
在 Java 程序设计语言中, 异常对象都是派生于 Throwable 类的一个实例 。 稍后还可以看到 , 如果 Java 中内置的异常类不能够满足需求 , 用户可以创建自己的异常类 。
所有的异常都是由 Throwable 继承而来, 但在下一层立即分解为两个分支: Error 和 Exception。
Error 类层次结构描述了 Java 运行时系统的内部错误和资源耗尽错误 。 应用程序不应该抛出这种类型的对象 。 如果出现了这样的内部错误, 除了通告给用户, 并尽力使程序安全地终止之外 , 再也无能为力了 。 这种情况很少出现 。
在设计 Java 程序时 , 需要关注 Exception 层次结构 。 这个层次结构又分解为两个分支 :一个分支派生于 RuntimeException ; 另一个分支包含其他异常。 划分两个分支的规则是 : 由程序错误导致的异常属于 RuntimeException ; 而程序本身没有问题 , 但由于像 I/O 错误这类问题导致的异常属于其他异常。
派生于 RuntimeException 的异常包含下面几种情况 :
错误的类型转换
数组访问越界
访问 null 指针
不是派生于 RuntimeException 的异常包括 :
试图在文件尾部后面读取数据。
试图打开一个不存在的文件 。
试图根据给定的字符串查找 Class 对象, 而这个字符串表示的类并不存在 。
如果出现 RuntimeException 异常, 那么就一定是你的问题” 是一条相当有道理的规则 。
Java 语 言 规 范 将 派 生 于 Error 类 或 RuntimeException 类的所有异常称为非受查( unchecked ) 异常 , 所有其他的异常称为受查 ( checked ) 异常 。
编译器将核查是否为所有的受査异常提供了异常处理器 。【即受查异常必须向上抛出或者捕获处理】
7.1.2 声明受查异常(keyword: throws)
注意:是受查异常,即 RuntimeException及其子类。
public Fi1elnputStream (String name) throws FileNotFoundException
什么异常必须使用 throws 子句声明 需要记住在遇到下面 4 种, 情况时应该抛出异常 :
1 ) 调用一个抛出受査异常的方法, 例如 , FilelnputStream 构造器 。
2 ) 程序运行过程中发现错误 并且利用 throw 语句抛出一个受查异常 (下一节将详细地,介绍 throw 语句 ) 。
3 ) 程序出现错误 , 例如, a [ - l ] =0 会抛出一个 ArraylndexOutOffloundsException 这样的非受查异常 。
4 ) Java 虚拟机和运行时库出现的内部错误 。
如果出现前两种情况之一, 则必须告诉调用这个方法的程序员有可能抛出异常 。 为什么 ? 因为任何一个抛出异常的方法都有可能是一个死亡陷阱 。 如果没有处理器捕获这个异常, 当前执行的线程就会结束 。
对于那些可能被他人使用的 Java 方法, 应该根据异常规范 ( exception specification ), 在方法的首部声明这个方法可能抛出的异常 。
7.1. 3 如何抛出异常
String readData(Scanner in) throws EOFException{
...
while(...)
{
if(!in.hasNext()) // EOF encountered
{
if(n < len)
throw new EOFException(); // 抛出异常
}
...
}
return s;
}
7.1 .4 创建异常类
找到合适的异常父类,直接继承即可。
java Jang.Throwabie 1.0 的几个重要方法
• Throwable( )
构造一个新的 Throwabie 对象, 这个对象没有详细的描述信息 。
• Throwable( String message )
构造一个新的 throwabie 对象, 这个对象带有特定的详细描述信息 。 习惯上, 所有派生的异常类都支持一个默认的构造器和一个带有详细描述信息的构造器 。
• String getMessage ( )
获得 Throwabie 对象的详细描述信息 。
7.2 捕获异常
通常 , 应该捕获那些知道如何处理的异常, 而将那些不知道怎样处理的异常继续进行传递 。
再次抛出异常与异常链;
try
{
access the database;
}
catch ( SQLException e)
{
// 这里, ServleException 用带有异常信息文本的构造器来构造 。
throw new ServletException ("database error: " + e . getMessageO ) ;
}
// 不过, 可以有一种更好的处理方法, 并且将原始异常设置为新异常的“原因”:
try{
access the database;
}catch(SQLException e){
Throwable se = new ServletException("database error");
se.initCause(e); // 使用原始异常的信息初始化新异常【保留了原始异常信息】
throw se;
}
/*
当捕获到异常时, 就可以使用下面这条语句重新得到原始异常 :
Throwable e = se.getCause();
强烈建议使用这种包装技术 。这样可以让用户抛出子系统中的高级异常,而不会丢失原始异
常的细节 。
*/
finally 子句:
无论代码是否有异常产生,最终都会去执行 finally 字句。
分析堆栈轨迹元素:
暂略
7.3 使用异常机制的技巧
下面给出使用异常机制的几个技巧:
1. 异常处理不能代替简单的测试
与执行简单的测试相比, 捕获异常所花费的时间大大超过了前者 , 因此使用异常的基本规则是 : 只在异常情况下使用异常机制 。[详情参考《Java核心技术卷第十版》P:283]
2 . 不要过分地细化异常
3. 利用异常层次结构
不要只抛出 RuntimeException 异常 。 应该寻找更加适当的子类或创建自己的异常类 。
不要只捕获 Thowable 异常 , 否则, 会使程序代码更难读 、 更难维护 。
4. 不要压制异常
5. 在检测错误时, “ 苛刻 ” 要比放任更好
6. 不要羞于传递异常