1. 程式人生 > >finally與return的執行順序

finally與return的執行順序

都知道,finally的執行特點

1、不管有木有出現異常,finally塊中程式碼都會執行;

2、當try和catch中有return時,finally仍然會執行。

那麼問題來了,執行順序是怎麼樣的呢?

一個簡單的測試類及反編譯後的位元組碼:

public class Test {
         publicstatic void main(String[] args) {
             System.out.println(test());
         }
 
         publicstatic int test() {
                   try{
                            System.out.println("Codesin try block.");
                            return0;
                   }catch (Exception e) {
                            System.out.println("Codesin catch block.");
                            return100;
                   }finally {
                            System.err.println("Codesin finally block.");
                   }
         }
}
/*
 public static int test();
   Code:
      0: getstatic     #2                  // Fieldjava/lang/System.out:Ljava/io/PrintStream;
      3: ldc           #5                  // String Codes in try block.
      5: invokevirtual #6                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      8: iconst_0
      9: istore_0
     10: getstatic     #7                  // Field java/lang/System.err:Ljava/io/PrintStream;
     13: ldc           #8                  // String Codes in finallyblock.
     15: invokevirtual #6                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
     18: iload_0
     19: ireturn
     20: astore_0
     21: getstatic     #2                  // Fieldjava/lang/System.out:Ljava/io/PrintStream;
     24: ldc           #10                 // String Codes in catchblock.
     26: invokevirtual #6                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
     29: bipush        100
     31: istore_1
     32: getstatic     #7                  // Fieldjava/lang/System.err:Ljava/io/PrintStream;
     35: ldc           #8                  // String Codes in finallyblock.
     37: invokevirtual #6                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
     40: iload_1
     41: ireturn
     42: astore_2
     43: getstatic     #7                  // Fieldjava/lang/System.err:Ljava/io/PrintStream;
     46: ldc           #8                  // String Codes in finallyblock.
     48: invokevirtual #6                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
     51: aload_2
     52: athrow
*/

可以將我們編寫的程式碼和編譯後的位元組碼做一下對比:


發現虛擬機器為我們做了好多事,它將finally語句塊插到try,catch與return語句之間了。這也就是為什麼無論異常與否,返回與否,finally都會執行的原因。既然,finally都會執行,那麼在finally中return什麼就是什麼。既然finally前面所有return都是無效的,return還有什麼意義呢,所以,實際中finally塊是不允許返回的。eclipse編譯器會提示warning:finallyblock does not complete normally。

幾種情形的測試程式碼

try{ return; }catch(){} finally{} return;

測試程式碼:

1.基本資料型別

   public static int test1(){
      int a = 10;
      try{
         a = 20;
         return a;
      }catch(Exception e){
         //other codes.
      }finally{
         a += 5;
         //other codes.
      }
      return a;
   }//最終返回值為20
2.引用資料型別改變引用物件的值
   public static StringBuffer test2(){
      StringBuffer sb = new StringBuffer("abc");
      try{
         return sb;
      }catch(Exception e){
         //other codes.
      }finally{
         sb.append("DEF");
         //other codes.
      }
      return sb;
   }//最終返回中內容為abcDEF
3.引用資料型別改變引用物件 
  public static StringBuffer test3(){
      StringBuffer sb = new StringBuffer("abc");
      try{
         return sb;
      }catch(Exception e){
         //other codes.
      }finally{
         sb = new StringBuffer("DEF");
         //other codes.
      }
      return sb;
   }//最終返回值中的內容為abc

在此種情形的測試中可以發現,在finally之前若有return語句,finally對返回變數無論做什麼修改,變數本身是不會再變的,比如為基本型別的變數重新賦值,或是為引用型別的變數重新指定引用都是不會記錄到返回值中的。但是finally中的語句會執行,那麼在finally中對引用型別的變數指向的物件內容是可以修改的,且修改有效。此種情形可以這樣理解,遇到return語句,虛擬機器為return的值蓋了一套房子,房子能隨便拆嗎?不能。但是房子裡面的人是可以變的。基本資料型別和引用型別的變數本身就是房子,引用型別變數指向的物件中的內容就是房子中的人。

有這樣一個小測試:

   public static int test(int num){
      int a = 10;
      try{
         return a;
      }catch(ArithmeticException e){
         a = 20;
         return a;
      }finally{
         a = 30;
         return a;
      }
   }
1.1 try 塊中沒有異常時返回值是多少?

1.2 try塊中有ArithmeticException異常時返回值是多少?

1.3 try塊中有其他異常時返回值是多少?

答案:全部是30.原因是在執行完try塊中return之前的程式碼後,jvm在return之間插入了finally塊中的程式碼,finally塊中含有return,那麼就直接返回了。