[Java] Flow control in try catch finally

在正常情況下,當執行完 try 的最後一行或是要把控制權轉交給它的呼叫者 (return 或丟出 exception)時,會執行 finally ,當 finally 執行完畢,再把控制權轉交給 try。

但是當在 finally 裡面寫下 return, throw exception, continue, break 會沒有辦法把控制權轉交給 try,造成不可以預期的結果。所以千萬不要在 finally 裡面 return 或丟出 exception,雖然現在的 IDE 都會聰明到幫你檢查出來。

主要原因 try-finally 轉成 Java byte code 時候,finally 是一個副程式,當 try 執行完畢時候就會去呼叫 finally 副程式。當 finally 副程式執行完畢,會跳到 try 的記憶體位置。但在 finally 裡面加了這些return, throw exception, continue, break,它就提前跳出程式回不到正確的位置。

另外,當 try 裡面強制中斷 JVM 或是非預期的情況發生提早結束 (這裡所指的非預期的情況不是丟出 exception,而是當下的 thread 突然死掉或是 JVM 突然有問題這類的。),finally 是不會執行的。

Try-finally clause

1
2
3
4
5
6
7
try {
// Block of code with multiple exit points
}
finally {
// Block of code that is always executed when the try block is exited,
// no matter how the try block is exited
}

Example 1

1
2
3
4
5
6
7
8
9
10
11
12
int i = 0; 
try {
i = i + 1;
System.out.println(i);
}
finally{
i = i + 2;
System.out.println(i);
}

//1
//3

Example 2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static void main(String[] args){
System.out.println(doSomeThing());
}

private static int doSomeThing(){
try {
return 1;
}
finally{
return 2;
}
}

//2

Example 3

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public static void main(String[] args) throws Exception {
System.out.println(doSomeThing(true));
}

private static int doSomeThing(boolean bVal) throws Exception {
try {
if (bVal) {
return 1;
}
return 0;
} finally {
System.out.println("Got old fashioned.");
}
}

// Got old fashioned.
// 1

Example 4

1
2
3
4
5
6
7
8
9
10
11
12
13
public static void main(String[] args) throws Exception {
System.out.println(doSomeThing());
}

private static int doSomeThing() throws Exception {
try {
throw new Exception("from try");
} finally {
throw new Exception("from finally");
}
}

// Exception in thread "main" java.lang.Exception: from finally

Example 5

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public static void main(String[] args) throws Exception {
System.out.println(doSomeThing(true));
}

private static int doSomeThing(boolean bVal) throws Exception {
int i = 0;
while (true) {
try {
try {
i = 1;
} finally { // the first finally clause
i = 2;
}
i = 3;
return i; // this never completes, because of the continue
} finally { // the second finally clause
if (i == 3) {
continue; // this continue overrides the return statement
}
}
}
}

Example 6

1
2
3
4
5
6
7
8
9
10
11
12
13
public static void main(String[] args) throws Exception {
System.out.println(doSomeThing());
}

private static int doSomeThing() throws Exception {
try {
System.exit(1);
} finally {
throw new Exception("from finally");
}
}

// terminates JVM and doesn't execute finally block

References

Try-finally clauses defined and demonstrated