你真的理解Java中的try/catch/finally嗎?

看幾個例子,回顧一下執行順序

例子1 無異常,finally中的return會致使提早返回

public static String test() {
   try {
       System.out.println("try");
       return "return in try";
  } catch(Exception e) {
       System.out.println("catch");
       return "return in catch";
  } finally {
       System.out.println("finally");
       return "return in finally";
  }
}

調用test()的結果:jvm

try
finally
return in finally

例子2 無異常,try中的return會致使提早返回

public static String test() {
   try {
       System.out.println("try");
       return "return in try";
  } catch(Exception e) {
       System.out.println("catch");
  } finally {
       System.out.println("finally");
  }
   return "return in function";
}

調用test()的結果:ide

try
finally
return in try

例子3 有異常,finally中的return會致使提早返回

public static String test() {
       try {
           System.out.println("try");
           throw new Exception();
      } catch(Exception e) {
           System.out.println("catch");
           return "return in catch";
      } finally {
           System.out.println("finally");
           return "return in finally";
      }
  }

調用test()的結果:函數

try
catch
finally
return in finally

例子4 有異常,catch中的return會致使提早返回

public static String test() {
   try {
       System.out.println("try");
       throw new Exception();
  } catch(Exception e) {
       System.out.println("catch");
       return "return in catch";
  } finally {
       System.out.println("finally");
  }
}

調用test()的結果:oop

try
catch
finally
return in catch

例子4 有異常,不會提早返回

public static String test() {
   try {
       System.out.println("try");
       throw new Exception();
  } catch(Exception e) {
       System.out.println("catch");
  } finally {
       System.out.println("finally");
  }
   return "return in function";
}

調用test()的結果:優化

try
catch
finally
return in function

小結

上面這幾個例子,大多數人已經很是瞭解。同時也衍生出一些理論,好比不要在finally中return等,再也不贅述。atom

 

再看幾個例子,返回值是否符合你的預期?

例子1

public static int test() {
   try {
       return 1;
  } finally {
       return 2;
  }
}

返回值:2spa

說明:與咱們上面的例子一致,finally中的return致使提早返回,try中的return1不會被執行。.net

附編譯後的代碼:orm

public static int test() {
   try {
       boolean var0 = true;
       return 2;
  } finally {
      ;
  }
}

能夠看到編譯器作過優化,同時驗證了boolean類型在底層是用int實現的,但注意你在源碼中直接給int行賦值true或false是不被容許的。對象

例子2

public static int test() {
   int i;
   try {
       i = 3;
  } finally {
       i = 5;
  }
   return i;
}

返回值:5

說明:執行try中的代碼後,再執行finally中的代碼,最終i被賦值爲5,最後返回

附編譯後的代碼:

public static int test() {
   boolean var0 = true;
   byte i;
   try {
       var0 = true;
  } finally {
       i = 5;
  }
   return i;
}

一樣能夠看出,編譯器作了一些優化。

例子3

public static int test() {
   int i = 1;
   try {
       i = 3;
       return i;
  } finally {
       i = 5;
  }
}

返回值:3

這個例子稍微有點意思,按咱們一般的思惟,應該仍是返回5,畢竟finally中把i賦值爲5了嘛,而後由try中的return返回。然而很不幸,返回值是3。

爲何呢?先看一下編譯後的代碼:

public static int test() {
   boolean var0 = true;
   byte var1;
   try {
       int i = 3;
       var1 = i;
  } finally {
       var0 = true;
  }
   return var1;
}

咱們會發現,finally中的代碼塊不起做用。不知你是否想起一點:Java中是按值傳遞的,finally中的i只是一個局部變量,finally塊執行完畢後,局部變量便不復存在。

接着看例子:

例子4

public static List test() {
   List<Integer> list = new ArrayList<>();
   try {
       list.add(1);
       return list;
  } finally {
       list.add(2);
  }
}

返回:包含1和2兩個元素的List對象。

說明:這個例子中,基本類型int被替換爲引用類型List,雖然list是按值傳遞,但它內部的狀態可變(體如今這裏,就是能夠add元素)。擴展:finally只能保證對象自己不可變,但沒法保證對象內部狀態不可變。

附編譯後的代碼:

public static List test() {
   ArrayList list = new ArrayList();
   ArrayList var1;
   try {
       list.add(1);
       var1 = list; // 執行這一步操做後,var1和list指向同一個對象
  } finally {
       list.add(2);
  }
   return var1;
}

你如今應該以爲本身理解了,那麼再來看兩個例子:

例子5

public static int test() {
    try {
        System.exit(0);
    } finally {
        return 2;
    }
}

該函數沒有返回值。緣由:jvm提早退出了。

附編譯後的代碼:

public static int test() {
   try {
       System.exit(0);
       return 2;
  } finally {
      ;
  }
}

例子6

public static int test() {
   try {
       while(true) {
          System.out.println("Infinite loop.");
      }
  } finally {
       return 2;
  }
}

因爲try中的無限循環阻塞,永遠執行不到finally中的代碼塊。

附編譯後的代碼:

public static int test() {
   try {
       while(true) {
           System.out.println("Infinite loop.");
      }
  } finally {
      ;
  }
}

小結

爲了方便說明,只舉了finally代碼塊的例子,catch代碼塊是相似的。

總結

執行順序:

  1. try代碼塊中return前面的部分

  2. catch代碼塊中return前面的部分

  3. finally代碼塊中return前面的部分

  4. finally的return 或 catch的return 或 try的return。若前面的return被執行,會致使提早返回,同時後面的return被忽略。

  5. 方法的其餘部分

變量:

  注意Java的按值傳遞規則

特殊狀況:

  注意finally不會被執行的狀況   

參考

Try-catch-finally-return clarification [duplicate]

有return的狀況下try catch finally的執行順序(最有說服力的總結)

相關文章
相關標籤/搜索