finally塊不被執行的情況總結
2019-08-03 22:23:02 by沖沖
finally塊的作用
通常用於處理善后工作。當try塊里出現異常時,會立即跳出try塊,到catch塊匹配對應的異常,執行catch塊里的語句。此時,可能在try塊里存在打開的文件沒關閉,連接的網絡沒斷開,這部分資源是GC所不能自動處理的,所以finally的作用就是將它們及時釋放回收。
finally塊不被執行的情況,總共有3種:不進入try塊、程序中止、線程中止(帶finally塊的是守護線程,其非守護線程都執行完畢)。
1. 未執行try塊
對於try-catch-finally或者try-finally,如果不能進入try塊,則finally塊也無法進入。
1 public class Test { 2 public static void main(String[] args) { 3 boolean flag = false; 4 if(flag) { 5 try { 6 System.out.println("enter try block"); 7 } finally { 8 System.out.println("enter finally block"); 9 } 10 } 11 } 12 } 13 /****************** 14 控制台無輸出 15 *******************/
2. System.exit()
System.exit()的作用是中止當前虛擬機,如果虛擬機被中止,程序也會被終止,finally代碼塊自然不會執行。
1 public class Test { 2 public static void main(String[] args) { 3 try { 4 System.out.println("enter try block"); 5 System.exit(); 6 } finally { 7 System.out.println("enter finally block"); 8 } 9 } 10 } 11 /***************** 12 控制台打印如下 13 enter try block 14 ******************/
3. try塊陷入無限循環
1 public class Test { 2 public static void main(String[] args) { 3 try { 4 while(true){ 5 System.out.println("enter try block"); 6 } 7 } finally { 8 System.out.println("enter finally block"); 9 } 10 } 11 } 12 /***************** 13 完蛋 14 *****************/
4. 守護(daemon)線程被中止時
Java線程分為兩類,守護線程和非守護線程。當所有的非守護線程都終止時,無論守護線程存不存在,虛擬機都會kill掉所有的守護線程從而中止程序。
虛擬機中,執行main方法的線程就是一個非守護線程,垃圾回收則是另一個守護線程,main執行完,則程序中止,而不管垃圾回收線程是否中止。
所以,如果守護線程中存在finally代碼塊,那么當所有的非守護線程中止時,守護線程被kill掉,其finally代碼塊是不會執行的。
1 public class Test { 2 public static void main(String[] args) { 3 //main是一個非守護線程 4 Thread thread = new Thread(new Task()); 5 thread.setDaemon(true); //設置thread為守護線程 6 thread.start(); 7 TimeUnit.SECONDS.sleep(5); //阻塞5s. 8 } 9 } 10 class Task implements Runnable { 11 @Override 12 public void run() { 13 System.out.println("enter run()"); 14 try { 15 System.out.println("enter try block"); 16 } catch(InterruptedException e) { 17 System.out.println("enter catch block"); 18 } finally { 19 System.out.println("enter finally block"); 20 } 21 } 22 } 23 /******************* 24 控制台打印如下 25 enter run() 26 enter try block 27 enter try finally block 28 ********************/
上述代碼,語句 TimeUnit.SECONDS.sleep(5); 會使main線程阻塞5秒,足夠線程thread執行。
如果將該語句注釋,非守護線程main線程執行完 thread.start(); 這行后,存在三種情況:①CPU時間片還是交給main線程,則非守護線程執行完畢,守護線程thread就會被終止,finally塊不執行;②CPU時間片交給thread線程,但是thread線程剛執行完try塊,就得交付時間片給main,main已經無語句執行,就會結束,導致守護線程thread也要結束,finally塊不執行;③CPU時間片交付thread線程,thread線程完全執行,finally塊被執行。
4. 其他迷惑性選項
(1)當try塊里面包含有break,該次try塊結束后,finally塊也會執行。
1 public class Test { 2 public static void main(String[] args) { 3 for (int i = 0; i < 5; i++) { 4 try { 5 if (i == 2) { 6 break; 7 } 8 } finally { 9 System.out.print(i); 10 } 11 } 12 } 13 } 14 15 /************* 16 輸出結果:012 17 **************/
(2)當try塊里面包含有return,該次try塊結束后,finally塊也會執行。
1 public class Test { 2 public static void main(String[] args) { 3 for (int i = 0; i < 5; i++) { 4 try { 5 if (i == 2) { 6 return; 7 } 8 } finally { 9 System.out.print(i); 10 } 11 } 12 } 13 } 14 15 /************* 16 輸出結果:012 17 **************/
(3)當try塊里面包含有continue,該次try塊結束后,finally塊也會執行。
1 public class Test { 2 public static void main(String[] args) { 3 for (int i = 0; i < 5; i++) { 4 try { 5 if (i == 2) { 6 continue; 7 } 8 } finally { 9 System.out.print(i); 10 } 11 } 12 } 13 } 14 15 /************* 16 輸出結果:01234 17 **************/