「本文已参与好文召集令活动,点击查看:后端、大前端双赛道投稿,2万元奖池等你挑战!」前端
java作业务开发同窗常常会使用try catch finally捕捉异常,而使用起来有还有三种组合,try finally 和 try catch 和 try catch finally。java
try{
System.out.println("业务执行");
int i = 1 / 0;
System.out.println("业务执行完成");
}finally {
System.out.println("释放资源");
}
复制代码
有的同窗可能就要问了,写了try不写catch,还有这样写的吗? 这样写的意义是啥???后端
try{
System.out.println("业务执行");
int i = 1 / 0;
System.out.println("业务执行完成");
}catch (Exception e){
System.out.println("捕捉异常");
}
复制代码
try catch finally 这种通常是咱们平时用的作多写法markdown
try {
System.out.println("业务执行");
} catch (Exception e) {
System.out.println("捕捉异常");
} finally {
System.out.println("释放资源");
}
复制代码
既然java的语法支持这样写,那么就有它的用法。在这里我贴一段大名鼎鼎的线程池 的执行Work线程的代码,这里让大家看下这样的用法在java基础框架中被大量运用 ,只是咱们只是还知道罢了。框架
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
while (task != null || (task = getTask()) != null) {
w.lock();
// If pool is stopping, ensure thread is interrupted;
// if not, ensure thread is not interrupted. This
// requires a recheck in second case to deal with
// shutdownNow race while clearing interrupt
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() &&
runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
wt.interrupt();
try {
beforeExecute(wt, task);
try {
task.run();
afterExecute(task, null);
} catch (Throwable ex) {
afterExecute(task, ex);
throw ex;//抛出异常
}
//这里没有finnaly 由于没有最后必定须要释放的资源
} finally {
// 这里没有catch异常,主要缘由是为后面的
// completedAbruptly变量用来判断执行任务是否抛出异常
//1.若是上面的代码抛出了异常,那么必定是程序里层的try部分 // 里task执行,捕捉的异常,而后抛出的异常,此时程序是走不到
//completedAbruptly = false;这一行,由于外层try是
// 没有加catch异常的,
//2.若是程序能completedAbruptly=false这一行,那么此时
//程序外层try住部分是没有异常发生的。表明执行任务正常状况。
task = null;
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally {
processWorkerExit(w, completedAbruptly);
}
}
复制代码
public static int test() {
int i = 0;
try {
System.out.println("业务执行");
i = 1 / 0;
System.out.println("业务执行完成");
i++;
return i;
} catch (Exception e) {
System.out.println("捕捉异常");
i++;
return i;
} finally {
System.out.println("释放资源");
i++;
return i;
}
}
复制代码
执行结果以下:函数
下面是上面的函数javap反编译出来的JVM的字节码,因为指令比较长,我会加好注释方便理解ui
public static int test();
descriptor: ()I
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=4, args_size=0
0: iconst_0 // 将常量0推送到操做数栈
1: istore_0 // 将操做数栈顶元素(这里是0) 存储到局部变量表的0的位置(注意: 这里局部变量表0通常是存放是this指针,因为这里是static方法,因此没有this指针)
2: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream; // 获取 PrintStream的静态变量
5: ldc #25 // String 业务执行
7: invokevirtual #27 // Method java/io/PrintStream.println:(Ljava/lang/String;)V //调用打印函数
10: iconst_1 // 将常量 1 推送到操做数栈顶
11: iconst_0 // 将常量 0 推送到操做数栈顶
12: idiv // 取出操做数栈两个数进行除法运算
13: istore_0 //将运算结果保存到函数的局部变量表中第0个位置。
14: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream;
17: ldc #30 // String "业务执行完成"常量推送到操做数栈顶
19: invokevirtual #27 // Method java/io/PrintStream.println:(Ljava/lang/String;)V //pop操做数顶元素,调用打印函数,
22: iinc 0, 1 // 执行局部变量表中索引0号位置数据加1
25: iload_0 // 执行局部变量表中索引0号位置数据推送到操做数栈
26: istore_1 // 将操做数栈顶元素保存到局部变量表的1号位置
27: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream;
30: ldc #32 // String 释放资源
32: invokevirtual #27 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 调用函数打印"释放资源"
35: iinc 0, 1 // 执行局部变量表中索引0号位置数据加1
38: iload_0 // 执行局部变量表中索引0号位置数据推送到操做数栈
39: ireturn // 执行返回
40: astore_1 // 将操做数栈顶元素保存到局部变量表索引为1的位置
41: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream;
44: ldc #36 // String 捕捉异常
46: invokevirtual #27 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
49: iinc 0, 1 // 执行局部变量表中索引0号位置数据加1
52: iload_0 // 执行局部变量表中索引0号位置数据推送到操做数栈
53: istore_2 // 将操做数栈顶元素保存到局部变量表索引为2的位置
54: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream;
57: ldc #32 // String 释放资源
59: invokevirtual #27 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 打印函数
62: iinc 0, 1 // 执行局部变量表中索引0号位置数据加1
65: iload_0 // 执行局部变量表中索引0号位置数据推送到操做数栈
66: ireturn //执行返回
67: astore_3 // 将操做数栈顶元素保存到局部变量表索引为2的位置
68: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream;
71: ldc #32 // String 释放资源
73: invokevirtual #27 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
76: iinc 0, 1 // 执行局部变量表中索引0号位置数据加1
79: iload_0 // 执行局部变量表中索引0号位置数据推送到操做数栈
80: ireturn //执行返回操做数栈的元素
Exception table:
from to target type
2 27 40 Class java/lang/Exception
2 27 67 any
40 54 67 any
LineNumberTable:
line 27: 0
line 29: 2
line 30: 10
line 31: 14
line 32: 22
line 33: 25
line 39: 27
line 40: 35
line 41: 38
line 34: 40
line 35: 41
line 36: 49
line 37: 52
line 39: 54
line 40: 62
line 41: 65
line 39: 67
line 40: 76
line 41: 79
LocalVariableTable:
Start Length Slot Name Signature
41 26 1 e Ljava/lang/Exception;
2 79 0 i I
StackMapTable: number_of_entries = 2
frame_type = 255 /* full_frame */
offset_delta = 40
locals = [ int ]
stack = [ class java/lang/Exception ]
frame_type = 90 /* same_locals_1_stack_item */
stack = [ class java/lang/Throwable ]
复制代码
执行结果是 2,因为抛出异常因此try块里的i++没有执行,首先执行catch的i++,而后 执行finally的i++,而后执行返回,而catch的return从字节码里面都是是忽略的,由于 finally已经有return了 那么一样的程序,将finally中return去掉会怎么样this
public static int test() {
int i = 0;
try {
System.out.println("业务执行");
i = 1 / 0;
System.out.println("业务执行完成");
i++;
return i;
} catch (Exception e) {
System.out.println("捕捉异常");
i++;
return i;
} finally {
System.out.println("释放资源");
i++;
}
}
复制代码
执行结果变成了1,这是为何呢?spa
public static int test();
descriptor: ()I
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=4, args_size=0
0: iconst_0
1: istore_0
2: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream;
5: ldc #25 // String 业务执行
7: invokevirtual #27 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
10: iconst_1
11: iconst_0
12: idiv
13: istore_0
14: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream;
17: ldc #30 // String 业务执行完成
19: invokevirtual #27 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
22: iinc 0, 1
25: iload_0
26: istore_1
27: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream;
30: ldc #32 // String 释放资源
32: invokevirtual #27 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
35: iinc 0, 1
38: iload_1
39: ireturn
40: astore_1
41: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream;
44: ldc #36 // String 捕捉异常
46: invokevirtual #27 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
49: iinc 0, 1
52: iload_0
53: istore_2
54: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream;
57: ldc #32 // String 释放资源
59: invokevirtual #27 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
62: iinc 0, 1
65: iload_2
66: ireturn
67: astore_3
68: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream;
71: ldc #32 // String 释放资源
73: invokevirtual #27 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
76: iinc 0, 1
79: aload_3
80: athrow
Exception table:
from to target type
2 27 40 Class java/lang/Exception
2 27 67 any
40 54 67 any
复制代码
只是由于是因为try里面的return没有执行到,那么最终执行return由catch中执行返回, 从上图的字节码指令,catch的执行是将本地变量表中索引位置为1的位置加1后,而后load 到操做数栈,最后store到本地变量表的索引为2的位置。线程
而在执行finally里面的执行时索引变量0的索引此时是1加1,变成2,而最后执行会执行ireturn是前的操做,是从iload_2是本地变量表的2号位置拿的返回值,因此finally 里面的执行i++,只是改变了本地变量表的0号位置值,并不改变返回值。
本文主要总结java基础try catch finally的执行原理的过程,但愿以后本身写代码和看别人的代码,能明白其中道理。