try……catch……finally還有return

關(guān)于try……finally問題中return的問題,看到很多面試機(jī)經(jīng)有著不同的說法,這里配合javap對字節(jié)碼進(jìn)行反匯編之后仔細(xì)分析一番。

這是正常場景,try中return的情況:


public classTest1 {
         public static void main(String[] args) {
                   System.out.println(tryReturn());
         }
         static String tryReturn() {
                   String x = "hello";
                   String y = "你好";
                   try {
                            return x;
                   }
                   finally {
                            System.out.println(x);
                            x = "hi";
                            System.out.println(x);
                   }
         }

publictestGit.Test1();
    Code:
       0: aload_0
       1: invokespecial #8                  // Methodjava/lang/Object."":()V
       4: return
  public static void main(java.lang.String[]);
    Code:
       0: getstatic     #16                 // Fieldjava/lang/System.out:Ljava/io/PrintStream;
       3: invokestatic  #22                 // MethodtryReturn:()Ljava/lang/String;
       6: invokevirtual #26                 // Methodjava/io/PrintStream.println:(Ljava/lang/String;)V
       9: return
  static java.lang.String tryReturn();
    Code:
       0: ldc           #34                 // String hello
       2: astore_0
       3: ldc           #36                 // String你好
       5: astore_1
       6: aload_0
       7: astore_3
       8: getstatic     #16                 // Fieldjava/lang/System.out:Ljava/io/PrintStream;
      11: aload_0
      12: invokevirtual #26                 // Methodjava/io/PrintStream.println:(Ljava/lang/String;)V
      15:ldc           #38                 // String hi
      17: astore_0
      18: getstatic     #16                 // Fieldjava/lang/System.out:Ljava/io/PrintStream;
      21: aload_0
      22: invokevirtual #26                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      25: aload_3
      26: areturn                                                       //關(guān)注這個指令碼,它代表return
      27: astore_2
      28: getstatic     #16                 // Fieldjava/lang/System.out:Ljava/io/PrintStream;
      31: aload_0
      32: invokevirtual #26                 // Methodjava/io/PrintStream.println:(Ljava/lang/String;)V
      35: ldc           #38                 // String hi
      37: astore_0
      38: getstatic     #16                 // Fieldjava/lang/System.out:Ljava/io/PrintStream;
      41: aload_0
      42: invokevirtual #26                 // Methodjava/io/PrintStream.println:(Ljava/lang/String;)V
      45: aload_2
      46: athrow
    Exception table:
       from   to  target type
           6    8    27   any
}

首先明確一點,即使try中return了,try-catch-finally代碼塊也會執(zhí)行到結(jié)尾,所以finally{}中的代碼是一定會被執(zhí)行的。

然后注意字節(jié)碼指令areturn,它代表return。我們發(fā)現(xiàn)正常情況下,要return的對象被裝入了一個新的空間astore_3,同時執(zhí)行返回時也是通過aload_3讀取這個拷貝。換句話說正常情況下,finally中的代碼對局部變量的操作不會影響返回值,因為要return的變量是try中變量的副本。

注意,這里也是一個坑所在的地方。變量傳遞的時候都是值傳遞,所以如果變量存放的是指向?qū)ο蟮膔eference,那么這里就會有個問題,雖然我在finally中修改變量的值不會影響返回的結(jié)果,但是我在finally中對方法要返回的對象進(jìn)行操作時可以的。注意這段代碼:

publicclass Test1 {
         public static void main(String[] args){
                   System.out.println(tryReturn().getName());
         }
         static A tryReturn() {
                   A a = new A();
                   try {
                            a.setName("beijing");
                            return a;
                   }
                   finally {
                            System.out.println(a.getName());
                            a.setName("shanghai");
                            System.out.println(a.getName());//這里成功將a.name變成了上海,最后主函數(shù)打印時將顯示上海
                   }
         }
}
classA{
         private String name;
         public void setName(String s) {
                   name = s;
         }
         public String getName() {
                   return name;
         }
}

Finally中的代碼成功修改了a中的成員變量,這是要明確注意的。

最后,我們討論一下finally中也有return的情況:

這是在finally中加入return的情況

publicclassTest1{
         publicstaticvoidmain(String[] args) {
                   System.out.println(tryReturn());
         }
         staticStringtryReturn() {
                   Stringx= "hello";
                   Stringy= "你好";
                   try{
                            return x;
                   }
                   finally{//這么寫IDE是要給你警告的
                            System.out.println(x);
                            x = "hi";
                            System.out.println(x);
                            return x;
                   }
         }
}

publicclass testGit.Test1 {
  public testGit.Test1();
    Code:
       0: aload_0
       1: invokespecial #8                  // Methodjava/lang/Object."":()V
       4: return
  public static void main(java.lang.String[]);
    Code:
       0: getstatic     #16                 // Fieldjava/lang/System.out:Ljava/io/PrintStream;
       3: invokestatic  #22                 // MethodtryReturn:()Ljava/lang/String;
       6: invokevirtual #26                 // Methodjava/io/PrintStream.println:(Ljava/lang/String;)V
       9: return
  static java.lang.String tryReturn();
    Code:
       0: ldc           #34                 // String hello
       2: astore_0
       3: ldc           #36                 // String你好
       5: astore_1
       6: goto          10
       9: pop
      10: getstatic     #16                 // Fieldjava/lang/System.out:Ljava/io/PrintStream;
      13: aload_0
      14: invokevirtual #26                 // Methodjava/io/PrintStream.println:(Ljava/lang/String;)V
      17: ldc           #38                 // String hi
      19: astore_0
      20: getstatic     #16                 // Fieldjava/lang/System.out:Ljava/io/PrintStream;
      23: aload_0
      24: invokevirtual #26                 // Methodjava/io/PrintStream.println:(Ljava/lang/String;)V
      27: aload_0
      28: areturn
    Exception table:
       from   to  target type
           6    9     9   any
}

注意字節(jié)碼,try中的return被跳過去了。根據(jù)底部Exception table的提示,try中的代碼塊應(yīng)該是code:6、9這兩行。然而并沒有執(zhí)行areturn,而是執(zhí)行了goto直接跳到了finally的部分。換句話說如果try和finally中都有return語句,編譯器會忽視try中的return而選擇finally中的return。當(dāng)然如果你try和finally都寫return了,IDE多半是要給你黃線警告的。

結(jié)論:

  1. try……catch……finally結(jié)構(gòu)中,代碼一定會按流程執(zhí)行到結(jié)尾,當(dāng)然沒有異常被捕捉到的時候catch里的代碼是不執(zhí)行的。

  2. 關(guān)于finally在return之前還是之后。細(xì)扣的話,我認(rèn)為finally中的代碼生效在return之后,方法結(jié)束之前。這點從ex1中的字節(jié)碼文件中可以看到。當(dāng)然如果將return定義成方法結(jié)束的話,finally也的確就是發(fā)生在return之前的。

  3. 關(guān)于finally中代碼對返回值的影響。簡單地說,如果返回值是基本類型或者string,finally中任何關(guān)于局部變量的操作都不會影響到返回值;如果返回值是在此之外的對象,請一定注意finally中是可以對對象進(jìn)行操作的。

  4. 關(guān)于try和finally中都有return的情況。一般說來IDE會提醒你不要這么寫(eclipse和IDEA都會給你警告,當(dāng)然編譯器是能接受這種寫法的),如果你這么寫了,try中的return語句會被編譯器直接跳過。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容