Java语言中的异常处理
31 Aug 2013java异常分类
异常类层次
Throwable
Java中所以的异常都是有Throwable继承而来。
Error
Error类层次结构描述了JAVA运行时系统的内部错误和资源耗尽错误。应用程序不应该抛出这种类型的错误对象。如果程序出现了这样的内部错误,除了通告给用户,并尽力使程序安全终止之外,再无能为力。这种情况很少出现。
Exception
在设计JAVA程序时,需要关注Exception层次结构。这个层次结构又分为两个分支:
Runtime Exception
由程序错误导致的异常属于Runtime Exception,相当于C++中的logic_error类。如果出现“Runtime Exception”异常,那么就一定是你的问题。应该通过检测数组下表是否越界来避免ArrayIndexOutOfBoundsException异常;应该通过在使用变量之前检测是否为空来杜绝NullPointerException异常的发生。派生于Runtime Exception的异常包含下面几种情况:
- 错误的类型转换
- 数组访问越界
- 访问空指针
IOException
派生于IOException的异常包含下面几种情况:
- 试图在文件尾部后面读取数据
- 试图打开一个错误格式的URL
- 试图根据给定的字符串查找Class对象,而这个字符串表示的类并不存在
未检查异常和已检查异常
未检查异常
Java语言规范将派生于Error类或RuntimeException类的所以异常称为“未检查异常(unchecked exception)”。
已检查异常
除上面所述的未检查异常外的所以异常都称为“已检查异常(checked exception)”。编译器将核查是否为所有的已检查异常提供了异常处理器。
声明异常
方法应该在其首部使用throws关键字声明所有可能抛出的异常。例如:
public FileInputStream(String name) throws FileNotFoundException
当一个方法有可能抛出多个已检查异常时,那么必须在方法的首部列出所有的异常类。例如:
class MyClass {
public void MyMethod(String s) throws EOFException, IOException {
//... ...
}
}
在自己编写的方法时,不必将所有可能抛出的异常都进行声明。至于什么时候需要在方法中用throws字句声明异常,什么异常必须使用throws字句声明,需要记住在遇到下面4种情况时应该抛出异常:
- 调用一个抛出已检查异常的方法时,例如,FileInputStream
- 程序运行过程中发现错误,并且利用throws语句抛出一个已检查异常
- 程序出现错误,例如,a[-1] = 0会抛出一个ArrayIndexOutOfBoundsException这样的未检查异常
- Java虚拟机和运行库出现内部异常
如果出现前两种情况之一,则必须告诉调用这个方法的程序员有可能抛出异常。但是,并不需要声明Java的内部错误,即那些从Error继承而来的错误。任何程序代码都具有抛出那些异常的潜能,而我们对其没有任何的控制能力。同样的,也不应该声明从RuntimeException继承的那些未检查异常。例如:
class MyClass {
public void MyMethod(String s) throws ArrayIndexOutOfBoundsException //这里不必要做声明未检查异常
}
总之一个方法必须声明所有可能抛出的已检查异常,而未检查异常要么不可控制(Error),要么就应该避免(RuntingException)。如果方法没有声明所有可能发生的已检查异常,编译器就会给出一个错误信息。
子类中的异常
如果在子类中覆盖了超类的一个方法,子类方法中声明的异检查异常不能超过超类方法中声明的异常范围(也就是说,子类中抛出的异常范围更加小,或者根本不抛出异常)。
抛出异常
抛出一个已经存在的异常类,一般步骤如下:
- 找到一个合适的异常类
- 创建这个类的一个对象
- 将对象抛出
例如:
void MyMethod(Scanner in) throws EOFException {
while(...) {
if(!in.hasNext) {//EOF encountered
if(n < len)
throw new EOFException();
}
}
}
创建异常
在程序中,可能会遇到任何标准异常类都没有能够充分描述清楚的问题。在这种情况下,我们可以定义一个派生于Exception或其子类的类,例如:
class FileFormatException extends IOException {
public MyException() {}
public MyException(String str) {
super(str);
}
}
使用MyException:
string readData(BufferedReader in) throws FileFormatException {
...
while(...) {
if(ch == -1){ //EOF encourntered
if(n < len)
throw new FileFormatException();
}
}
return s;
}
捕获异常
Java中捕获异常的方法跟C++中一样,可以使用try catch字句。
捕获单个异常
public void read(String filename) {
try{
InputStream in = new InputStream(filename);
int b;
while((b = in.read()) != -1) {
...
}
} catch(IOException e) {
e.printStackTrace();
} finally {
in.close();
}
}
捕获多个异常
在一个try语句块中可以捕获多个异常类型,并对不同类型异常做不同的处理,例如:
try {
//可能抛出异常的代码
} catch(MalformatURLException e1) {
//处理MalformatURLException
} catch(UnknownHostException e2) {
//处理UnknowHostException
} catch(IOException e3) {
//处理IOException
} finally {
//一些抛出异常后的处理
}
与C++语言的比较
- C++中的logic_error类相当于Java中的RuntimeException
- C++中的runtime_error类相当于Java中非RuntimeExcepton
- C++ 中的throw与Java中的throws基本相同,但有一点重要的区别。在C++中,throw说明符是在运行时执行,而不是在编译时执行,也就是说,C++编译器将不处理任何异常说明符
- C++中如果不给出throw声明,函数可能抛出任何异常。Java中如果没有throws说明符,函数将不能抛出任何异常检查
- C++中可以抛出任何类型的值,Java中只能抛出Throwable子类的对象
ref:《Java 核心技术》