JAVA try catch finally return 執行順序
JAVA try catch finally return 執行順序
參考:https://www.cnblogs.com/superFish2016/p/6687549.html
一、結論
1、不管有沒有出現異常,finally塊中程式碼都會執行;
2、當try和catch中有return時,finally仍然會執行;
3、如果try、catch中有return語句,finally是在return後面的表示式運算後執行的。
- 如果是基本資料型別、靜態變數。(此時並沒有返回finally中運算後的值,而是先把要返回的值儲存起來,不管finally中的程式碼怎麼樣,返回的值都不會改變,仍然是之前儲存的值),所以函式返回值是在finally執行前確定的;
- 如果是引用資料型別,那麼在finally中修改除包裝型別,會對try、catch中返回的變數有影響。
原因:JAVA基本資料型別、物件的引用以及方法引數存放在棧中,引用物件本身放在堆中。- 1 暫存器:最快的儲存區,由編譯器根據需求進行分配,我們在程式中無法控制。
- 2 棧:存放基本型別的變數資料和物件的引用以及方法引數,但物件本身不存放在棧中,而是存放在堆(new出來的物件)或者常量池中(字串常量物件存放在常量池中。)
- 3 堆:存放所有new出來的物件。
- 4 靜態域:存放靜態成員(static定義的)
- 5 常量池:存放字串常量和基本型別常量(public static final)。
4、儘量不要在finally中使用return語句,如果使用的話,會忽略try、catch中的返回語句,也會忽略try、catch中的異常,遮蔽了錯誤的發生。
5、finally中避免再次丟擲異常,一旦finally中發生異常,程式碼執行將會丟擲finally中的異常資訊,try、catch中的異常將被忽略
所以在實際專案中,finally常常是用來關閉流或者資料庫資源的,並不額外做其他操作。
二、return
return語句用來結束迴圈(函式),或返回一個函式的值。
1 return, 如果什麼都不接的話,其實就是void型別函式的返回,返回後不再執行return後面的語句
如果函式執行成功返回0,不成功返回非0,一般情況下非0值常用-1來表示。
2 return 0:一般用在主函式結束時,表示程式正常終止,即告訴系統程式正常。
3 return -1::表示返回一個代數值,一般用在子函式結尾。表示程式異常終止,即告訴系統程式異常
4 return 1:與return -1相同。
三、程式碼例項
3.1基本資料型別和static型別
1、沒有異常、沒有return
public static int testBasic1() {// 10
int i = 1;
try {
i++;
System.out.println("1try block, i = " + i);// 1try block, i = 2
} catch (Exception e) {
i++;
System.out.println("1catch block i = " + i);// 沒輸出
} finally {
i = 10;
System.out.println("1finally block i = " + i);// 1finally block i = 10
}
return i;// 10
}
2、沒有異常、有return
執行順序:try return有一個返回值 執行finally
- 在轉去之前,try中先把要返回的結果存放到不同於i的區域性變數中去,執行完finally之後,在從中取出返回結果,
- 因此,即使finally中對變數i進行了改變,但是不會影響返回結果。
- 它應該使用棧儲存返回值。
public static int testBasic2() {//不進入catch 返回的是2
int i = 1;
try {
i++;
System.out.println("2try block, i = " + i);//2try block, i = 2
return i;
} catch (Exception e) {
i++;
System.out.println("2catch block i = " + i);//沒輸出
return i;
} finally {
i = 10;
System.out.println("2finally block i = " + i);//2 finally block i = 10
// return i; 有警告
}
}
- 程式碼順序執行從try到finally,由於finally是無論如何都會執行的,所以try裡的語句並不會直接返回。在try語句的return塊中,return返回的引用變數並不是try語句外定義的引用變數i,而是系統重新定義了一個區域性引用i’,這個引用指向了引用i對應的值,也就是2,即使在finally語句中把引用i指向了值10,因為return返回的引用已經不是i,而是i’,所以引用i的值和try語句中的返回值無關了。
3、有異常、有return
執行順序:try-異常(try中異常後的不執行)-catch-- return有一個返回值 執行finally
- 在轉去之前,catch中先把要返回的結果存放到不同於i的區域性變數中去,執行完finally之後,在從中取出返回結果,
- 因此,即使finally中對變數i進行了改變,但是不會影響返回結果。
- 它應該使用棧儲存返回值。
public static int testBasic3() {//進入catch 返回的是3
int i = 1;
try {
i++;
int j = 1/0;//丟擲異常 算術運算異常
System.out.println("3try block, i = " + i);//不執行
return i;
} catch (Exception e) {
i++;
System.out.println("3catch block i = " + i);//輸出 3catch block i = 3
return i;
} finally {
i = 10;
System.out.println("3finally block i = " + i);//3finally block i = 10
// return i; //有警告
}
}
4、finally中有return
public static int testBasic4() {//沒異常不進入catch,有異常進入catch 兩種情況返回的都是finally的10
int i = 1;
try {
i++;
// int j = 1/0;//丟擲異常 算術運算異常
System.out.println("4try block, i = " + i);//不丟擲異常有輸出4try block, i = 2 //丟擲異常沒輸出
return i;
} catch (Exception e) {
i++;
System.out.println("4catch block i = " + i);//不丟擲異常沒輸出 //丟擲異常 有輸出4catch block i = 3
return i;
} finally {
i = 10;
System.out.println("4finally block i = " + i);//finally block i = 10
return i; //有警告 finally中不要有返回值
}
}
5、finally中有異常
public static int testBasic5(){//finally中有異常 控制檯報錯 Exception in thread "main" java.lang.ArithmeticException: / by zero
int i = 1;
try{
i++;
Integer.parseInt(null);
System.out.println("5try block, i = "+i);
return i;
}catch(Exception e){
String.valueOf(null);
System.out.println("5catch block i = "+i);
return i;
}finally{
i = 10;
int m = i / 0;//Exception in thread "main" java.lang.ArithmeticException: / by zero
System.out.println("5finally block i = "+i);
}
}
3.2引用資料型別
1、沒有異常、finally無return。引用資料型別
public static List<Object> testWrap(){;//返回[try, finally]
List<Object> list = new ArrayList<>();
try{
list.add("try");
System.out.println("try block");//try block
return list;
}catch(Exception e){
list.add("catch");
System.out.println("catch block");
return list;
}finally{
list.add("finally");
System.out.println("finally block ");//finally block
}
}
- 可以看到,finally裡對list集合的操作生效了,這是為什麼呢。我們知道基本型別在棧中儲存,而對於非基本型別是儲存在堆中的,返回的是堆中的地址,因此內容被改變了。
2、有異常、finally無return。引用資料型別
public static List<Object> testWrap2() {// 返回[try, catch, finally]
List<Object> list = new ArrayList<>();
try {
list.add("try");
int i = 1/0;
System.out.println("try block");// 不輸出
return list;
} catch (Exception e) {
list.add("catch");
System.out.println("catch block");//輸出 catch block
return list;
} finally {
list.add("finally");
System.out.println("finally block ");// finally block
}
}
- 可以看到,finally裡對list集合的操作生效了,這是為什麼呢。我們知道基本型別在棧中儲存,而對於非基本型別是儲存在堆中的,返回的是堆中的地址,因此內容被改變了。
總結
- 舉例:
- 情況1:try{} catch(){}finally{} return;
- 顯然程式按順序執行。
- 情況2:try{ return; }catch(){} finally{} return;
- 程式執行try塊中return之前(包括return語句中的表示式運算)程式碼;
- 再執行finally塊,最後執行try中return;
- finally塊之後的語句return,因為程式在try中已經return所以不再執行。
- 情況3:try{ } catch(){return;} finally{} return;
- 程式先執行try,如果遇到異常執行catch塊,
- 有異常:則執行catch中return之前(包括return語句中的表示式運算)程式碼,
再執行finally語句中全部程式碼,
最後執行catch塊中return. finally之後也就是4處的程式碼不再執行。 - 無異常:執行完try再finally再return.
- 情況4:try{ return; }catch(){} finally{return;}
- 程式執行try塊中return之前(包括return語句中的表示式運算)程式碼;
再執行finally塊,因為finally塊中有return所以提前退出。
- 程式執行try塊中return之前(包括return語句中的表示式運算)程式碼;
- 情況5:try{} catch(){return;}finally{return;}
- 沒有異常:程式先執行try,然後執行finally,然後return;
- 有異常:程式執行catch塊中return之前(包括return語句中的表示式運算)程式碼;
再執行finally塊,因為finally塊中有return所以提前退出(不執行catch中的return)。
- 情況6:try{ return;}catch(){return;} finally{return;}
- 程式執行try塊中return之前(包括return語句中的表示式運算)程式碼;
- 有異常:執行catch塊中return之前(包括return語句中的表示式運算)程式碼;
則再執行finally塊,因為finally塊中有return所以提前退出。 - 無異常:則再執行finally塊,因為finally塊中有return所以提前退出。
最終結論:
任何執行try 或者catch中的return語句之前,都會先執行finally語句,如果finally存在的話。
如果finally中有return語句,那麼程式就return了,所以finally中的return是一定會被return的,
編譯器把finally中的return實現為一個warning。