try-catch-finally 简介

在 Java 中,try-catch-finally 语句可用于错误捕获和错误处理。程序员将可能会抛出异常的代码置于 try 块中,使用 catch 来捕获不同类型的错误,并在 catch 块中编写错误处理代码。最后可以使用 finally 块来清理相关资源。

try 块内的代码抛出异常,就可能被 catch 捕获到,最后运行 finally;而若 try 中代码不抛出异常,最后也应该运行 finally 块中的代码。

但是在一些特殊情况下,try-catch-finally 代码的顺序变得难以确定。此文为这些可能的特殊情况做了一个简单的整理与总结。

异常情况总结

try 中含有 return

try 中产生异常

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

    private static void throwException() throws Exception {
        throw new Exception("throwing Exception");
    }

    private static String catchException() {
        try {
            System.out.println("try block");
            throwException();
            return "try return";
        } catch (Exception e) {
            System.out.printf("catch the exception: `%s` in catch block\n", e.toString());
        } finally {
            System.out.println("finally block");
        }

        return "outside the try-catch-finally block";
    }
}

运行结果为:

1
2
3
4
try block
catch the exception: `java.lang.Exception: throwing Exception` in catch block
finally block
outside the try-catch-finally block

可见,运行顺序遵循 try -> catch -> finally ,在抛出异常后,会直接运行到 catch 中的语句,而不会运行 try 中的 return 语句。这与我们的预期一致。

try 中不产生异常

即:能够运行到 try 中的 return 语句。

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

    private static String noException() {
        try {
            System.out.println("try block");
            return "try return";
        } catch (Exception e) {
            System.out.printf("catch the exception: `%s` in catch block\n", e.toString());
        } finally {
            System.out.println("finally block");
        }

        return "outside the try-catch-finally block";
    }
}

运行结果为:

1
2
3
try block
finally block
try return

可见,当 try 中没有异常抛出时,在 try 中的 return 语句被执行时会运行 finally 中的代码,再返回 tryreturn 语句的返回值。

try, catch, finally 中都有 return

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

    private static String allReturn() {
        try {
            System.out.println("try block");
            return "try return";
        } catch (Exception e) {
            System.out.printf("catch the exception: `%s` in catch block\n", e.toString());
            return "catch return";
        } finally {
            System.out.println("finally block");
            return "finally return";
        }
    }
}

运行结果为:

1
2
3
try block
finally block
finally return

finally 中有 return 语句,则会直接从 finally 中返回结果,try 块中的 return 结果则被丢弃。

catch, finally 抛出异常

catch 抛出异常

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

    private static void throwException() throws Exception {
        throw new Exception("throwing Exception");
    }

    private static String catchThrowException() {
        try {
            System.out.println("try block");
            throwException();
        } catch (Exception e) {
            System.out.printf("catch the exception: `%s` in catch block\n", e.toString());
            int a = 2 / 0; // ArithmeticException
            System.out.println("catch end");
        } finally {
            System.out.println("finally block");
        }

        return "outside the try-catch-finally block";
    }
}

运行结果为:

1
2
3
4
5
6
7
try block
catch the exception: `java.lang.Exception: throwing Exception` in catch block
finally block

Exception in thread "main" java.lang.ArithmeticException: / by zero
	at tryCatchFinally.catchFinallyException.catchThrowException(catchFinallyException.java:18)
	at tryCatchFinally.catchFinallyException.main(catchFinallyException.java:5)

可见,catch 块在抛出异常后终止,最后 main 线程会抛出异常,但是 finally 块仍然运行。

finally 抛出异常

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

    private static void throwException() throws Exception {
        throw new Exception("throwing Exception");
    }

    private static String catchFinallyException() {
        try {
            System.out.println("try block");
            throwException();
        } catch (Exception e) {
            System.out.printf("catch the exception: `%s` in catch block\n", e.toString());
        } finally {
            System.out.println("finally block");
            int a = 2 / 0; // ArithmeticException
            System.out.println("finally end");
        }

        return "outside the try-catch-finally block";
    }
}

运行结果为:

1
2
3
4
5
6
7
try block
catch the exception: `java.lang.Exception: throwing Exception` in catch block
finally block

Exception in thread "main" java.lang.ArithmeticException: / by zero
	at tryCatchFinally.catchFinallyException.catchFinallyException(catchFinallyException.java:20)
	at tryCatchFinally.catchFinallyException.main(catchFinallyException.java:5)

可见,finally 块运行到抛出异常后终止,最后 main 线程也会抛出异常。

try, catch, finally 中有 continue

try 中有 continue

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

    private static String tryContinue() {
        for (int i = 0; i < 2; i++) {
            try {
                System.out.println("try block");
                continue;
            } catch (Exception e) {
                System.out.printf("catch the exception: `%s` in catch block\n", e.toString());
            } finally {
                System.out.println("finally block");
            }
        }

        return "outside the try-catch-finally block";
    }
}

运行结果为:

1
2
3
4
5
try block
finally block
try block
finally block
outside the try-catch-finally block

当代码运行到 trycontinue 后,执行 finally 块中的逻辑再继续循环。

catch 中有 continue

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
public class withContinue {
    public static void main(String[] args) {
        System.out.println(catchContinue());
    }
    
    private static String catchContinue() {
        for (int i = 0; i < 2; i++) {
            try {
                System.out.println("try block");
                throw new Exception("throwing exception");
            } catch (Exception e) {
                System.out.printf("catch the exception: `%s` in catch block\n", e.toString());
                continue;
            } finally {
                System.out.println("finally block");
            }
        }

        return "outside the try-catch-finally block";
    }
}

运行结果为:

1
2
3
4
5
6
7
try block
catch the exception: `java.lang.Exception: throwing exception` in catch block
finally block
try block
catch the exception: `java.lang.Exception: throwing exception` in catch block
finally block
outside the try-catch-finally block

可见,仍然会继续运行 finally 块中的代码再进行下次循环。

finally 中有 continue

catch 块中抛出异常,观察 finallycontinue 的运行现象。

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

    private static String finallyContinue() throws Exception {
        for (int i = 0; i < 2; i++) {
            try {
                System.out.println("try block");
                throw new Exception("throwing exception");
            } catch (Exception e) {
                System.out.println("catch block, throwing exception");
                throw new Exception("throwing exception");
            } finally {
                System.out.println("finally block");
                continue;
            }
        }

        return "outside the try-catch-finally block";
    }
}

运行结果为:

1
2
3
4
5
6
7
try block
catch block, throwing exception
finally block
try block
catch block, throwing exception
finally block
outside the try-catch-finally block

主线程并不会捕获到异常,因为 finally 中的 continue 导致 catch 中的异常无法被抛出。

try, catch, finally 中有 break

try 中有 break

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
public class withBreak {
    public static void main(String[] args) throws Exception {
        System.out.println(tryBreak());
    }
    private static String tryBreak() {
        for (int i = 0; i < 2; i++) {
            try {
                System.out.println("try block");
                break;
            } catch (Exception e) {
                System.out.printf("catch the exception: `%s` in catch block\n", e.toString());
            } finally {
                System.out.println("finally block");
            }
        }

        return "outside the try-catch-finally block";
    }
}

运行结果为:

1
2
3
try block
finally block
outside the try-catch-finally block

当代码运行到 trybreak 后,执行 finally 块中的逻辑跳出循环。

catch 中有 break

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
public class withBreak {
    public static void main(String[] args) throws Exception {
        System.out.println(catchBreak());
    }
    
    private static String catchBreak() {
        for (int i = 0; i < 2; i++) {
            try {
                System.out.println("try block");
                throw new Exception("throwing exception");
            } catch (Exception e) {
                System.out.printf("catch the exception: `%s` in catch block\n", e.toString());
                break;
            } finally {
                System.out.println("finally block");
            }
        }

        return "outside the try-catch-finally block";
    }
}

运行结果为:

1
2
3
4
try block
catch the exception: `java.lang.Exception: throwing exception` in catch block
finally block
outside the try-catch-finally block

catch 中捕获异常后仍然会继续运行 finally 块中的代码再跳出循环。

finally 中有 break

catch 块中抛出异常,观察 finallybreak 的运行现象。

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

    private static String finallyBreak() throws Exception {
        for (int i = 0; i < 2; i++) {
            try {
                System.out.println("try block");
                throw new Exception("throwing exception");
            } catch (Exception e) {
                System.out.println("catch block, throwing exception");
                throw new Exception("throwing exception");
            } finally {
                System.out.println("finally block");
                break;
            }
        }

        return "outside the try-catch-finally block";
    }
}

运行结果为:

1
2
3
4
try block
catch block, throwing exception
finally block
outside the try-catch-finally block

finally 中有 continue 的结果类似。主线程并不会捕获到异常,因为 finally 中的 break 导致 catch 中的异常无法被抛出。

try, catch 中有 exit

trycatch 中有 System.exit() 时,JVM 将被关闭,finally 中的代码将不会被运行。

try 中有 exit

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

    private static String tryExit() {
        try {
            System.out.println("try block");
            System.exit(1);
        } catch (Exception e) {
            System.out.printf("catch the exception: `%s` in catch block\n", e.toString());
        } finally {
            System.out.println("finally block");
        }

        return "outside the try-catch-finally block";
    }
}

运行结果为:

1
try block

可见,程序直接从 try 块中的 System.exit(1) 退出了,没有执行 finally 块的内容。

catch 中有 exit

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

    private static void throwException() throws Exception {
        throw new Exception("throwing Exception");
    }

    private static String catchExit() {
        try {
            System.out.println("try block");
            throwException();
        } catch (Exception e) {
            System.out.printf("catch the exception: `%s` in catch block\n", e.toString());
            System.exit(1);
        } finally {
            System.out.println("finally block");
        }

        return "outside the try-catch-finally block";
    }
}

运行结果为:

1
2
try block
catch the exception: `java.lang.Exception: throwing Exception` in catch block

程序直接从 catch 块中的 System.exit(1) 退出了,没有执行 finally 块的内容。

守护进程

当程序中所有用户线程都终止时,JVM 会终止所有相关守护线程。

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

    private static String daemonThread() {
        Thread thread = new Thread(() -> {
            try {
                System.out.println("try block");
                Thread.sleep(2000);
                System.out.println("try end");
            } catch (InterruptedException e) {
                System.out.println("catch block");
            } finally {
                System.out.println("finally block");
            }
        });
        thread.setDaemon(true);
        thread.start();

        return "main thread end";
    }
}

新建一个守护线程,并在其 try 块中设置一个执行时间很久的任务(这里用 sleep 代替)。主线程将守护进程启动后,即可退出。

运行结果为:

1
2
main thread end
try block

可见其在主线程退出后,守护线程也被终止了。而且值得注意的是,守护线程并没有执行 finally 块,而是直接被 JVM 终止(就如运行 System.exit() )。

参考


知识共享许可协议
本作品采用知识共享署名-相同方式共享 4.0 国际许可协议进行许可。