文章目录
本文参考:
try catch finally 用法 - aspirant - 博客园 (cnblogs.com)
基础知识
try { //执行的代码,其中可能有异常。一旦发现异常,则立即跳到catch执行。否则不会执行catch里面的内容 }
catch { //除非try里面执行代码发生了异常,否则这里的代码不会执行 }
finally { //不管什么情况都会执行,包括try catch 里面用了return }
catch可以有多个,也可以没有,每个catch可以处理一个特定的异常。程序按照你catch的顺序查找异常处理块,如果找到,则进行处理,如果找不到,则向上一层次抛出。如果没有上一层次,则向用户抛出,此时,如果你在调试,程序将中断运行,如果是部署的程序,将会中止。
finally可以没有,也可以只有一个。即使你在try块内用return返回了,在返回前,finally总是要执行,这以便让你有机会能够在异常处理最后做一些清理工作,如关闭数据库连接等等。
基本使用
1. 基本的try-catch-finally使用,不带异常
package com.jxz.trycatch;
import org.junit.Test;
/**
* @Author jiangxuzhao
* @Description
* @Date 2023/6/17
*/
public class TryCatchTest {
public static void main(String[] args) {
System.out.println(f1());
}
public static String f1(){
try{
int i = 1/1;
}catch (Exception e){
System.out.println("catch exception...");
e.printStackTrace();
}finally {
System.out.println("finally..."); // finally无论如何都会执行
}
System.out.println("main continue..."); // 没有异常可以继续向下执行
return "f1"; // 最后方法返回f1
}
}
输出:
finally...
main continue...
f1
2. 基本的try-catch-finally使用,带异常
public static String f2(){
try{
int i = 1/0;
}catch (Exception e){
System.out.println("catch exception..."); // 捕获到异常
e.printStackTrace();
}finally {
System.out.println("finally..."); // finally无论如何都会执行
}
System.out.println("main continue..."); // 捕获完异常可以继续向下执行
return "f2"; // 最后方法返回f2
}
输出:
catch exception...
finally...
main continue...
f2
java.lang.ArithmeticException: / by zero
at com.jxz.trycatch.TryCatchTest.f2(TryCatchTest.java:32)
at com.jxz.trycatch.TryCatchTest.main(TryCatchTest.java:14)
带return的各种情况
总结:
即使try-catch中有return,也都会执行finally语句块;先执行try或者catch中的return(return中自带逻辑也要执行完),但不返回,将变量存入临时栈中,然后去执行finally语句块;如果finally中有return,则程序会在finally执行完后直接return了;如果没有,则会执行完finally后返回前面临时栈中的值。
参考try、catch、finally、return执行顺序超详解析(针对面试题)_来盘海参炒面不要面的博客-优快云博客
1. try{ return; }catch(){} finally{} return;
-
先执行try块中return 语句(包括return语句中的表达式运算),但不返回;
-
执行finally语句中全部代码
-
最后执行try中return返回
示例:无异常
package com.jxz.trycatch;
/**
* @Author jiangxuzhao
* @Description
* @Date 2023/6/18
*/
public class TryCatchFinallyReturnTest {
public static void main(String[] args) {
System.out.println(f1());
// System.out.println(f2()); 下面的示例代码都自带这个打印
// System.out.println(f3());
}
public static int f1(){
int temp = 1;
try {
System.out.println(temp);
return temp;
} catch (Exception e) {
System.out.println(temp);
} finally {
++ temp;
System.out.println(temp);
}
return temp;
}
}
输出:121
先执行try中的打印为1,执行return,但是不返回;然后执行finally块,++temp,temp更新为2,同时打印为2;由于finally块中没有return语句,因此要返回try块中,返回其临时栈的值1,外层打印1.
2. try{} catch(){return;} finally{} return;
程序先执行try,如果遇到异常执行catch块,最终都会执行finally中的代码块;
- 有异常:
- 执行catch中的语句和return中的表达式运算,但在那时不返回
- 执行finally语句中全部代码,
- 最后执行catch块中return返回。 finally块后的return语句不再执行。
- 无异常:执行完try再finally再return…
示例1:有异常
public static int f2(){
int temp = 1;
try {
System.out.println(temp);
int i = 1/0;
} catch (Exception e) {
System.out.println(temp);
return ++ temp;
} finally {
++ temp;
System.out.println(temp);
}
return temp;
}
输出:1132
先执行try中的打印为1;出现异常进入catch块,先打印1,然后执行return ++temp,temp目前为2,但是仍然不返回;最后执行finally块,++temp,此时temp更新为3,同时打印3;由于finally块中没有return语句,因此要返回catch块中,返回其临时栈中的值2,外层打印2.
示例2:无异常
public static int f3(){
int temp = 1;
try {
System.out.println(temp);
// int i = 1/0;
} catch (Exception e) {
System.out.println(temp);
return ++ temp;
} finally {
++ temp;
System.out.println(temp);
}
return temp;
}
输出:122
先执行try中的打印为1;再执行finally,++temp,temp更新为2,并打印为2;此时temp为2,返回为2,打印为2.
3. try{ return; }catch(){} finally{return;}
- 执行try块中的代码,和return语句(包括return语句中的表达式运算),但不返回(try中return的表达式运算的结果放在临时栈);
- 再执行finally块,
- 执行finally块(和return中的表达式运算,字节码中装载此刻的变量),从这里返回。
此时finally块的return值,就是代码执行完后的值
示例: 无异常
public static int f4() {
int temp = 1;
try {
System.out.println(temp);
return temp;
} catch (Exception e) {
System.out.println(temp);
} finally {
System.out.println(temp);
return ++temp;
}
}
输出:112
先执行try中的打印为1,和return语句,但是并不返回,然后执行fially块,打印temp为1,执行return语句,++temp,temp更新为2并返回外层;外层打印为2.
4. try{} catch(){return;}finally{return;}
- 执行try中的语句块,
- 有无异常
- 有异常:程序执行catch块中return语句(包括return语句中的表达式运算,,并将结果保存到临时栈),但不返回;
- 无异常:直接执行下面的
- 再执行finally块,
- 执行finally块(和return中的表达式运算,字节码中装载此刻的变量),从这里返回。
示例1:无异常:
public static int f5() {
int temp = 1;
try {
System.out.println(temp);
} catch (Exception e) {
System.out.println(temp);
return ++temp;
} finally {
System.out.println(temp);
return ++temp;
}
}
输出: 112
先执行try中的打印1;然后执行finally块中的打印1,++temp,temp更新为2,并从这里返回,外层打印2.
有异常:
public static int f6() {
int temp = 1;
try {
System.out.println(temp);
int i = 1/0;
} catch (Exception e) {
System.out.println(temp);
return ++temp;
} finally {
System.out.println(temp);
return ++temp;
}
}
输出: 1123
先执行try中的打印1;出现异常然后执行catch中的打印1,++temp,temp更新为2,将结果存起来没有返回;执行finally中的打印为2,++temp,temp更新为3,这里finally块中有return直接从此处返回,外层打印3.
字节码原理
- 从上面的实验可以看出来,finally总会执行,那么jvm底层是如何实现的呢?
参考java异常处理(二)—从字节码层面看throw关键字及try…catch…finally的实现_程序猿成长轨迹的博客-优快云博客,其实底层就是将finally块的字节码复制了三份,这三份代码分别放在了不同位置
- try块最后(如果try中有ireturn,则在ireturn前)
- catch块最后(如果catch中有ireturn,则在ireturn前)
- 位于异常执行路径:如果try中有异常但没有被catch捕获,或者catch又抛异常,那么就执行最终的finally代码
- 上面带return的各种情况在jvm如何解释呢?
主要和字节码ireturn有关,参考try catch finally的底层原理_lwd512768098的博客-优快云博客,finally语句中是否带return,差别体现在字节码ireturn语句前是iload_0还是iload_1。
- 异常又是如何处理的呢?
- 带有try…catch的方法中,方法会携带一个异常表
- 异常表中每个条目都是一个异常处理器
- 触发异常时,JVM会遍历异常表,比较触发异常行数(方法开始的偏移量)是否在异常处理器from指针到to指针范围内
- 范围匹配后,会比较异常类型和异常处理器中的type是否相同
- 类型匹配后,会跳转到target指针所指向的字节码(catch代码块开始位置)
- 如果没有匹配到异常处理器,会弹出当前方法对应的Java栈帧,并对调用者重复操作