Loading

[Java基础]try-catch-finally

finally 中的代码一定会执行吗

通常在面试中,只要是疑问句一般答案都是"否定"的,因为如果是"确定"和"正常"的,那面试官就没有必要再问了。

典型回答

正常运行的情况下,finally 中的代码是一定会执行的,但是,如果遇到以下异常情况,那么 finally 中的代码就不会继续执行了:

程序在 try 块中遇到 System.exit() 方法,会立即终止程序的执行,这时 finally 块中的代码不会被执行,例如以下代码:

public class FinallyExample {
    public static void main(String[] args) {
        try {
            System.out.println("执行 try 代码.");
            System.exit(0);
        } finally {
            System.out.println("执行 finally 代码.");
        }
    }
}

以上程序的执行结果如下:

执行 try 代码

在 try 块中遇到 Runtime.getRuntime().halt() 代码,强制终止正在运行的 JVM。与 System.exit()方法不同,此方法不会触发 JVM 关闭序列。因此,当我们调用 halt 方法时,都不会执行关闭钩子或终结器。实现代码如下:

public class FinallyExample {
    public static void main(String[] args) {
        try {
            System.out.println("执行 try 代码.");
            Runtime.getRuntime().halt(0);
        } finally {
            System.out.println("执行 finally 代码.");
        }
    }
}

以上程序的执行结果如下:

执行 try 代码

程序在 try 块中遇到无限循环或者发生死锁等情况时,程序可能无法正常跳出 try 块,此时 finally 块中的代码也不会被执行。

掉电问题,程序还没有执行到 finally 就掉电了(停电了),那 finally 中的代码自然也不会执行。

JVM 异常崩溃问题导致程序不能继续执行,那么 finally 的代码也不会执行。

钩子方法解释

在编程中,钩子方法(Hook Method)是一种由父类提供的空或默认实现的方法,子类可以选择性地重写或扩展该方法,以实现特定的行为或定制化逻辑。钩子方法可以在父类中被调用,以提供一种可插拔的方式来影响父类的行为。
钩子方法通常用于框架或模板方法设计模式中。框架提供一个骨架或模板,其中包含一些已经实现的方法及预留的钩子方法。具体的子类可以通过重写钩子方法来插入定制逻辑,从而影响父类方法的实现方式。

考点分析

正常运行的情况下,finally 中的代码是一定会执行的,但是,如果遇到 System.exit() 方法或 Runtime.getRuntime().halt() 方法,或者是 try 中发生了死循环、死锁,遇到了掉电、JVM 崩溃等问题,那么 finally 中的代码也是不会执行的。

知识扩展

System.exit() 和 Runtime.getRuntime().halt() 都可以用于终止 Java 程序的执行,但它们之间有以下区别:

  • System.exit():来自 Java.lang.System 类的一个静态方法,它接受一个整数参数作为退出状态码,通常非零值表示异常终止,使用零值表示正常终止。其中,最重要的是使用 exit() 方法,会执行 JVM 关闭钩子或终结器。

  • Runtime.getRuntime().halt():来自 Runtime 类的一个实例方法,它接受一个整数参数作为退出状态码。其中退出状态码只是表示程序终止的原因,很少在程序终止时使用非零值。而使用 halt() 方法,不会执行 JVM 关闭钩子或终结器。

例如以下代码,使用 exit() 方法会执行 JVM 关闭钩子:

class ExitDemo {
    // 注册退出钩子程序
    static {
        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            System.out.println("执行 ShutdownHook 方法");
        }));
    }
    public static void main(String[] args) {
        try {
            System.out.println("执行 try 代码。");
            // 使用 System.exit() 退出程序
            System.exit(0);
        } finally {
            System.out.println("执行 finally 代码。");
        }
    }
}

以上程序的执行结果如下:

执行 try 代码。
执行 ShutdownHook 方法

而 halt() 退出的方法,并不会执行 JVM 关闭钩子,示例代码如下:

class ExitDemo {

    // 注册退出钩子程序
    static {
        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            System.out.println("执行 ShutdownHook 方法");
        }));
    }

    public static void main(String[] args) {
        try {
            System.out.println("执行 try 代码。");
            // 使用 Runtime.getRuntime().halt() 退出程序
            Runtime.getRuntime().halt(0);
        } finally {
            System.out.println("执行 finally 代码。");
        }
    }
}

以上程序的执行结果如下:

执行 try 代码。

小结

正常运行的情况下,finally 中的代码是一定会执行的,但是,如果遇到 System.exit() 方法或 Runtime.getRuntime().halt() 方法,或者是 try 中发生了死循环死锁,遇到了掉电JVM 崩溃等问题,finally 中的代码是不会执行的。而 exit() 方法会执行 JVM 关闭钩子方法或终结器,但 halt() 方法并不会执行钩子方法或终结器。

try-catch-finally 和 return 的执行顺序

先来看实验,结论在最后

没有返回值,没有错误

public class Main {
    public static void main(String[] args) {
        test();
    }
    public static void test() {
        try {
            System.out.println("try 打印 : ");
        } catch (Exception e) {
            System.out.println("catch 打印 : ");
        } finally {
            System.out.println("finally 打印 : ");
        }
    }
}
try 打印 :
finally 打印 :
进程已结束,退出代码为 0

try -> finally

没有返回值,有错误

public class Main {
    public static void main(String[] args) {
        test();
    }

    public static void test() {
        try {
            System.out.println("try 进入 : ");
            int a = 1/0;
            System.out.println("try 结束 : ");
        } catch (Exception e) {
            System.out.println("catch 进入 : ");
            System.out.println("catch 结束 : ");
        } finally {
            System.out.println("finally 进入 : ");
            System.out.println("finally 结束 : ");
        }
    }
}
try 进入 :
catch 进入 :
catch 结束 :
finally 进入 :
finally 结束 :
进程已结束,退出代码为 0

try -> try 出错直接从出错点跳到catch -> finally

有返回值,没有错误,返回值在try

public class Main {
    public static void main(String[] args) {
        System.out.println("test 返回 : " + test());
    }

    public static int test() {
        int a = 10;
        try {
            System.out.println("try 进入 : ");
            a++;
            System.out.println("try 结束 : ");
            return a;
        } catch (Exception e) {
            System.out.println("catch 进入 : ");
            a++;
            System.out.println("catch 结束 : ");
        } finally {
            System.out.println("finally 进入 : ");
            a++;
            System.out.println("finally 结束 : ");
        }
        return -1;
    }
}
try 进入 : 
try 结束 : 
finally 进入 : 
finally 结束 : 
test 返回 : 11

进程已结束,退出代码为 0

try -> finally -> try return

有返回值,没有错误,返回值在try,catch

public class Main {
    public static void main(String[] args) {
        System.out.println("test 返回 : " + test());
    }

    public static int test() {
        int a = 10;
        try {
            System.out.println("try 进入 : ");
            a++;
            System.out.println("try 结束 : ");
            return a;
        } catch (Exception e) {
            System.out.println("catch 进入 : ");
            a++;
            System.out.println("catch 结束 : ");
            return a;
        } finally {
            System.out.println("finally 进入 : ");
            a++;
            System.out.println("finally 结束 : ");
        }
    }
}
try 进入 : 
try 结束 : 
finally 进入 : 
finally 结束 : 
test 返回 : 11

进程已结束,退出代码为 0

try -> finally -> try return

有返回值,没有错误,返回值在try,catch,finally

public class Main {
    public static void main(String[] args) {
        System.out.println("test 返回 : " + test());
    }

    public static int test() {
        int a = 10;
        try {
            System.out.println("try 进入 : ");
            a++;
            System.out.println("try 结束 : ");
            return a;
        } catch (Exception e) {
            System.out.println("catch 进入 : ");
            a++;
            System.out.println("catch 结束 : ");
            return a;
        } finally {
            System.out.println("finally 进入 : ");
            a++;
            System.out.println("finally 结束 : ");
            return a;
        }
    }
}
try 进入 : 
try 结束 : 
finally 进入 : 
finally 结束 : 
test 返回 : 12

进程已结束,退出代码为 0

try -> finally -> finally return

有返回值,有错误,返回值在try

public class Main {
    public static void main(String[] args) {
        System.out.println("test 返回 : " + test());
    }

    public static int test() {
        int a = 10;
        try {
            System.out.println("try 进入 : ");
            a++;
            int b = 1 / 0;
            System.out.println("try 结束 : ");
            return a;
        } catch (Exception e) {
            System.out.println("catch 进入 : ");
            a++;
            System.out.println("catch 结束 : ");
        } finally {
            System.out.println("finally 进入 : ");
            a++;
            System.out.println("finally 结束 : ");
        }
        return -1;
    }
}
try 进入 : 
catch 进入 : 
catch 结束 : 
finally 进入 : 
finally 结束 : 
test 返回 : -1

进程已结束,退出代码为 0

try -> catch -> finally -> return -1

有返回值,有错误,返回值在try,catch

public class Main {
    public static void main(String[] args) {
        System.out.println("test 返回 : " + test());
    }

    public static int test() {
        int a = 10;
        try {
            System.out.println("try 进入 : ");
            a++;
            int b = 1 / 0;
            System.out.println("try 结束 : ");
            return a;
        } catch (Exception e) {
            System.out.println("catch 进入 : ");
            a++;
            System.out.println("catch 结束 : ");
            return a;
        } finally {
            System.out.println("finally 进入 : ");
            a++;
            System.out.println("finally 结束 : ");
        }
    }
}
try 进入 : 
catch 进入 : 
catch 结束 : 
finally 进入 : 
finally 结束 : 
test 返回 : 12
进程已结束,退出代码为 0

try -> catch -> finally -> catch return

有返回值,有错误,返回值在try,catch,finally

public class Main {
    public static void main(String[] args) {
        System.out.println("test 返回 : " + test());
    }

    public static int test() {
        int a = 10;
        try {
            System.out.println("try 进入 : ");
            a++;
            int b = 1/0;
            System.out.println("try 结束 : ");
            return a;
        } catch (Exception e) {
            System.out.println("catch 进入 : ");
            a++;
            System.out.println("catch 结束 : ");
            return a;
        } finally {
            System.out.println("finally 进入 : ");
            a++;
            System.out.println("finally 结束 : ");
            return a;
        }
    }
}
try 进入 :
catch 进入 :
catch 结束 :
finally 进入 :
finally 结束 :
test 返回 : 13

进程已结束,退出代码为 0

try -> catch -> finally -> finally return

finally中返回导致异常丢失

public class Main {
    public static void main(String[] args) {
        System.out.println("test 返回 : " + test());
    }

    public static int test() {
        try {
            System.out.println("try 进入 : ");
            int b = 1 / 0;
            System.out.println("try 结束 : ");
            return 0;
        } catch (Exception e) {
            System.out.println("catch 进入 : ");
            System.out.println(e);
            System.out.println("catch 结束 : ");
            return -1;
        } finally {
            System.out.println("finally 进入 : ");
            System.out.println("finally 结束 : ");
            return 1;
        }
    }
}
try 进入 : 
catch 进入 : 
java.lang.ArithmeticException: / by zero
catch 结束 : 
finally 进入 : 
finally 结束 : 
test 返回 : 1

我们本希望返回-1,却被1覆盖

public class Main {
    public static void main(String[] args) {
        System.out.println("test 返回 : " + test());
    }

    public static int test() {
        try {
            System.out.println("try 进入 : ");
            int b = 1 / 0;
            System.out.println("try 结束 : ");
            return 0;
        } catch (Exception e) {
            System.out.println("catch 进入 : ");
            System.out.println(e);
            System.out.println("catch 结束 : ");
            return -1;
        } finally {
            System.out.println("finally 进入 : ");
            System.out.println("finally 结束 : ");
        }
    }
}
try 进入 : 
catch 进入 : 
java.lang.ArithmeticException: / by zero
catch 结束 : 
finally 进入 : 
finally 结束 : 
test 返回 : -1

进程已结束,退出代码为 0

总结

  1. finally中有return,一定会走finally中的return,不提倡finally中return,因为从finally中返回会覆盖异常返回
  2. finally中没有return,
    如果没有错误,尝试从try return,如果try没有返回,那就尝试返回try块外部的return
    如果有错误,尝试从catch return,如果catch没有返回,那就尝试返回try块外部的return
  3. try catch finally中return的值,会暂存第一次执行到这个位置上的数据,最终返回的也是这个暂存值(如果暂存的是地址值,那地址中的内容可以被后续代码修改)。
posted @ 2024-07-12 21:38  Duancf  阅读(57)  评论(0)    收藏  举报