Java 异常
1、Throwable 类
Java 异常层次结构:
异常分类:
- 检查型异常:除非检查型异常以外的所有异常
- 非检查型异常:派生于
Error
类或RuntimeException
类的所有异常
一个方法必须声明或捕获所有可能抛出的检查型异常
,而非检查型异常
要么在你的控制之外(Error),要么从一开始就应该避免(RuntimeException)。
2、抛出异常
抛出EOFException
异常:
throw new EOFException();
3、处理异常
2.1 声明异常
在方法的首部声明这个方法可能抛出一个异常:
public FileInputStream(String name) throws FileNotFoundException
如果一个方法可能抛出多个检查型异常,那么需要在方法的首部列出所有的异常类:
public Image loadImage(String s) throws FileNotFoundException, EOFException
不应该声明从Error
和RuntimeException
继承的非检查型异常。
2.2 捕获异常
① try/catch/finally 语句
下面是关闭输入流的代码示例:
InputStream in = ...;
try {
try {
code that might throw exceptions
}
finally {
in.close();
}
} catch(IOException e) {
show error message
}
使用规则:
- 一个 catch 可以捕获多个异常,不过需要注意此时异常变量隐含为
final
变量,例如在以下子句体中不能为 e 赋其他值:catch (FileNotFoundException | UnknownHostException e) {…} - 可以在 catch 中将捕获的异常抛出,或者抛出一个新的异常,并使用
initCause
方法将原始异常设置为新异常的“原因”,当捕获到这个新异常时,我们可以使用getCause
方法获取原始异常 - 不管是否有异常被捕获,finally 子句中的代码都会执行(在方法返回之前执行)
- finally 用于
清理资源
,不要把改变控制流的语句(return,throw,break,continue)放在 finally 子句中
下面演示在 finally 中使用return
存在的问题:
public class Main {
public static String foo() {
try {
return "try!!!";
} finally {
return "finally!!!";
}
}
public static void main(String[] args) {
System.out.println(foo());
}
}
打印结果为finally!!!
,即 finally 中的 return 将会先于 try 中的 return 执行!!!
② try-with-resources 语句
在 Java 7 中,如果资源实现了AutoCloseable
接口,我们可以使用try-with-resources
语句处理资源:
try (Resource res = ...) {
work with res
}
try 块退出时,会自动的调用资源的close
方法,关闭资源。(close 是 AutoCloseable 接口中的方法)
使用规则:
- try-with-resources 语句可以有 catch 和 finally 子句,这些子句在关闭资源之后执行
- 在 Java 9 中,可以在 try 首部中提供之前声明的
final
或effective final
变量 - 如果 try 和 close 都抛出异常,则 close 方法抛出的异常会被抑制,并由
addSuppressed
方法增加到try
中的异常,如果想查看被抑制的异常,我们可以使用getSuppressed
方法获取被抑制的异常数组
我们对最后一点进行演示:
class Demo extends InputStream {
@Override
public int read() throws IOException {
return 0;
}
@Override
public void close() throws IOException {
super.close();
throw new IOException();
}
}
public class Main {
public static void foo() {
try(Demo demo = new Demo()) {
throw new EOFException();
} catch (Exception e) {
e.printStackTrace();
System.out.println(Arrays.toString(e.getSuppressed()));
}
}
public static void main(String[] args) {
foo();
}
}
输出结果:
③ 异常丢失
Java 的异常实现存在异常丢失
的问题,如下所示:
public class Main {
public static void foo() {
try {
throw new IOException();
} finally {
return;
}
}
public static void main(String[] args) {
foo();
}
}
运行上述代码,我们会发现:在 try 中抛出了异常,它却没有产生任何输出。
这就是异常丢失,如果我们在 finally 中也使用 throw 抛出一个异常,结果只会输出新抛出的异常,try 中的异常同样会丢失。
2.3 如何选择处理方式
一般经验是,要捕获那些你知道如何处理的异常,而继续传播那些你不知道怎样处理的异常。
4、创建异常类
当遇到标准的异常类无法描述清楚问题时,我们可以创建自己的异常类。自定义的异常类需要派生于 Exception 类或其子类,并且应该包含一个默认构造器和一个包含详细描述信息的构造器。
class FileFormatException extends IOException {
public FileFormatException() {}
public FileFormatException(String gripe) {
super(gripe);
}
}
如有错误,欢迎指正。.... .- ...- . .- -. .. -.-. . -.. .- -.-- -.-.--