前言
Java内置了一套异常处理机制,在Java中异常是一种class,可以在任何地方抛出,但只需要在上层捕获,这样就和方法调用分离了。
1. 用try…catch捕获异常
捕获异常时通常使用try...catch
语句,把可能发生异常的代码放到try {...}
中,然后使用catch
捕获对应的Exception
及其子类。
public class Main {
public static void main(String[] args) {
String info = "error";
try {
getException(info);
} catch (Exception e) {
e.printStackTrace();
}
}
private static void getException(String info) throws CoreRuntimeException{
if ("error".equals(info)) {
throw new CoreRuntimeException();
}
}
}
2. finally语句
无论是否有异常发生,如果我们都希望执行一些语句,例如清理工作,可以把正常执行的语句放到try中,总是会执行的语句放到finally中。
public class Main {
public static void main(String[] args) {
try {
System.out.println("Strart");
process();
} catch (IOException e) {
System.out.println("IO error");
} catch (Exception e) {
System.out.println("IO error");
} finally {
System.out.println("END");
}
}
static void process() {
//正常执行的语句
}
}
上述代码无论是否发生异常,都会执行System.out.println("END");
这条语句。
注意finally有几个特点:
- finally语句不是必须的,可写可不写;
- finally总是最后执行。
如果没有发生异常,就正常执行try { ... }
语句块,然后执行finally
。如果发生了异常,就中断执行try { ... }
语句块,然后跳转执行匹配的catch
语句块,最后执行finally
。
可见,finally
是用来保证一些代码必须执行的。
3. try、catch、finally中同时存在return语句
那如果 try、catch、finally中同时存在return语句,方法到底该返回什么值呢?看看下面几段代码:
情况一:try正常执行,try、finally中都有return
public class TestReturn {
public static void main(String[] args) {
int result = getResult01();
System.out.println(result);
}
public static int getResult01() {
int result;
try {
result = 10 / 1;
return result;
} catch (Exception e) {
result = 20;
return result;
} finally {
result = 30;
return result;
}
}
}
程序执行结果:
可见,就算try中有return且未抛出异常,但finally中的语句依旧会被执行。
情况二:try抛出异常,catch、finally中都有return
public class TestReturn {
public static void main(String[] args) {
int result = getResult01();
System.out.println(result);
}
public static int getResult01() {
int result;
try {
result = 10 / 0;
return result;
} catch (Exception e) {
result = 20;
return result;
} finally {
result = 30;
return result;
}
}
}
程序执行结果:
可见,就算try中抛出异常,且catch中有return,但finally中的语句依旧会被执行。
结合前两种情况,可以判断,无论try或catch中是否有return,finally都一定会执行。
情况三:finally中无return,但改变了result值
public class TestReturn {
public static void main(String[] args) {
int result = getResult01();
System.out.println(result);
}
public static int getResult01() {
int result;
try {
result = 10 / 1;
return result;
} catch (Exception e) {
result = 20;
return result;
} finally {
result = 30;
}
}
}
程序运行结果:
可见,try中有return, 会先将值暂存,无论finally语句中对该值做什么处理,最终返回的都是try语句中的暂存值。
情况四:finally中有return,但不改变result值
public class TestReturn{
public static void main(String[] args){
System.out.print(getNumber(0));
System.out.print(getNumber(1));
System.out.print(getNumber(2));
System.out.print(getNumber(4));
}
public static int getNumber(int num){
try{
int result = 2 / num;
return result;
}catch (Exception exception){
return 0;
}finally{
if(num == 0){
return -1;
}
if(num == 1){
return 1;
}
}
}
}
程序运行结果:
可见,当try与finally语句中均有return语句,会忽略try中的return,即最终方法返回的是finally对应的return内容。
4. 被屏蔽的异常
如果在执行finally
语句时抛出异常,那么原来在catch
中准备抛出的异常就“消失”了,因为只能抛出一个异常。没有被抛出的异常称为“被屏蔽”的异常(Suppressed Exception)。
- 如何保存所有的异常信息?方法是先用
origin
变量保存原始异常,然后调用Throwable.addSuppressed()
,把原始异常添加进来,最后在finally
抛出:
public class Main {
public static void main(String[] args) throws Exception {
Exception origin = null;
try {
System.out.println(Integer.parseInt("abc"));
} catch (Exception e) {
origin = e;
throw e;
} finally {
Exception e = new IllegalArgumentException();
if (origin != null) {
e.addSuppressed(origin);
}
throw e;
}
}
}
- 通过
Throwable.getSuppressed()
可以获取所有的Suppressed Exception
。 - 绝大多数情况下,在
finally
中不要抛出异常。因此,我们通常不需要关心Suppressed Exception(白学警告)。
结论
- 无论try中是否有return,finally都一定会执行。
- try中有return, 会先将值暂存,无论finally语句中对该值做什么处理,最终返回的都是try语句中的暂存值。
- 当try与finally语句中均有return语句,会忽略try中的return,即最终方法返回的是finally对应的return内容。
- 在catch中抛出异常,不会影响finally的执行。JVM会先执行finally,然后抛出异常。
- 调用
printStackTrace()
可以打印异常的传播栈,对于调试非常有用。 - 捕获异常并再次抛出新的异常时,应该持有原始异常信息。
参考资料:
异常处理-廖雪峰的官方网站