异常
2018-12-19 本文已影响0人
爱做梦的严重精神病患者
1.异常
所有的异常都是由Throwanle继承而来的,但是在下一层立即分解为两个分支:Error和Exception。
Error类层次结构描述了Java运行时系统的内部错误和资源耗尽错误。一般程序运行时不会抛出这种内部错误。
Exception层次结构又分解为两个分支:由程序错误导致的异常属于RuntimeException;而程序本身没有问题,但由于像I/O错误这类问题导致的异常属于IOException。
派生于RuntimeException的异常包括下面几种情况:
- 错误的类型转换
- 数组访问越界
- 访问null指针
如果出现RuntimeException的异常(非受查异常),那么一定是你的问题。
派生于IOException的异常包括:
- 试图在文件尾部后面读取数据
- 试图打开一个不存在的文件。
- 试图根据给定的字符串查找Class对象,而这个字符串表示的类并不存在。
派生于Error类或RuntimeException类的所有异常称为非受查异常,其余的异常(IOException)称为受查异常。
Java异常层次结构.jpg2.声明、抛出、捕获异常
- 声明异常
一个方法必须声明所有可能抛出的受查异常,而非受查异常要么不可控制(Error),要么就应该避免发生(RuntimeException)。
在方法的首部声明这个方法可能抛出的异常,如果一个方法有可能抛出多个受查异常类型,那么就必须在方法的首部列出所有的异常类。
class MyAnimation {
public Image loadImage(String s) throws FileNotFoundException, EOFException {
...
}
}
- 抛出异常
假设在程序中的某个逻辑分支,会发生一种不正常的情况,需要我们去抛出异常。(声明异常是在方法的首部,而抛出异常则是在方法的内部)
String readData(Scanner in) throws EOFException {
...
while(...) {
if(!in.hasNext()) {
if(n < len)
throw new EOFException();
}
}
}
- 捕获异常
当调用了声明或抛出了异常的方法时,需要对异常进行捕获。如果某个异常发生的时候没有在任何地方进行捕获,那么程序就会终止。
要想捕获一个异常,必须设置try/catch语句块。如果在try语句块中的任何代码抛出了一个在catch子句中说明的异常类,那么程序将跳过try语句块的其余代码,然后程序将执行catch子句中的代码。
try {
code
...
} catch(ExceptionType e) {
handle for this type
}
通常,我们应该捕获那些知道如何处理的异常,而将那些不知道怎样处理的异常继续进行传递(抛出)。
-
再次抛出异常
有时抛出的异常需要再次进行包装,在catch子句中可以抛出一个异常,这样做的目的是改变异常的类型。例如开发了一个供其他程序员使用的子系统,那么表示子系统故障的异常类型可能就需要用到包装。
try {
access the database
} catch (SQLException e) {
// 创建一个子系统异常
Throwable se = new ServeltException("database error");
se.initCause(e);
throw se;
}
这种包装技术可以让用户抛出子系统中的高级异常,而不会丢失原始异常的细节。
- finally子句
当代码抛出一个异常时,就会终止方法中剩余代码的处理,并退出这个方法的执行。如果方法获取了一些本地资源,那么就会产生资源回收问题。然而,不管是否有异常被捕获,finally子句中的代码都被执行。
对于获取了本地资源的语句,当处理异常时,强烈建议解耦try/catch和try/finally语句块。
InputStream in = ...;
try {
try{
code that might throw exceptions
} finally {
in.close();
}
} catch (IOException e) {
show error message
}
内层的try语句只有一个职责,就是确保关闭输入流。外层的try语句块也只有一个职责,就是确保报告出现的错误。这种设计方式不仅清楚,而且还具有一个功能,就是将会报告finally子句中出现的错误。
- 带资源的try语句
假设资源属于一个实现了AutoCloseable接口的类,Java为这种代码模式提供了一个很有用的快捷方式。
带资源的try语句(try-with-resources)的最简单形式为:
try(Resource res =...) {
work with res
}
try(Scanner in = new Scanner(new FileInputStream("/usr/share/dict/words")),
"UTF-8") {
while(in.hasNext())
System.out.println(in.next());
}
try块退出时,会自动调用res.close(),就好像使用了finally块一样。
- 早抛出,晚捕获
很多人都感觉应该捕获抛出的全部异常,其实传递异常比捕获这些异常更好。让高层次的方法通知用户发生了错误,或者放弃不成功的命令更加适宜。