「本文已參與好文召集令活動,點擊查看:後端、大前端雙賽道投稿,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的執行原理的過程,但願以後本身寫代碼和看別人的代碼,能明白其中道理。