Java:try finally块的执行

33

当try块中存在return;语句时,我对try-finally的执行流程感到困惑。在我理解中,finally块总是会被执行,即在返回调用方法之前。但是考虑以下简单代码:

public class TryCatchTest {
    public static void main(String[] args){
        System.out.println(test());
    }
    static int test(){
        int x = 1;
        try{
            return x;
        }
        finally{
            x = x + 1;
        }
    }
}

打印出的结果实际上是1。这是否意味着finally块没有被执行?有人能帮我解决吗?


1
为什么你不在finally块中添加System.out.println(),看看它是否被执行了?我认为它已经被执行了,但是返回的值是1。 - Andrew Logvinov
当使用try catch时,您可以“捕获”特定类型的异常(例如:IOException,NullPointerException等)。当特定异常发生并且您需要在catch之前运行一些代码时,finally语句会发生... - RicardoE
7
在您投赞成票时,这里有一些需要思考的内容:这里是一些需要思考的Java相关问题。 - keyser
1
@RicardoE - 不完全正确。无论如何,finally子句都会被执行(除了一些混乱的递归异常情况),如果try区域被进入。 - Hot Licks
为什么要询问块是否被执行。这就是打印语句的作用。 - keyser
1
@Keyser,正是因为像Rohit这样的人花时间解释发生了什么以及所有潜在的功能,以便每个人,而不仅仅是提问者,都能拥有更全面的理解,所以我们才能前进。 - Deactivator2
3个回答

29

当你从try块返回时,返回值将存储在该方法的堆栈帧中。之后执行finally块。

finally块中更改值不会改变已经存在于堆栈中的值。但是,如果你从finally块再次返回,堆栈中的返回值将被覆盖,并且新的x将被返回。

如果你在finally块中打印x的值,你将会知道它何时被执行,并且x的值将被打印出来。

static int test(){
    int x = 1;
    try{
        return x;
    }
    finally{
        x = x + 1;
        System.out.println(x);  // Prints new value of x
    }
}

注意: 如果返回一个引用值,那么该引用的值将被存储在堆栈中。在这种情况下,您可以使用该引用来更改对象的值。


StringBuilder builder = new StringBuilder("");
try {
    builder.append("Rohit ");
    return builder;

} finally {
    // Here you are changing the object pointed to by the reference
    builder.append("Jain");  // Return value will be `Rohit Jain`

    // However this will not nullify the return value. 
    // The value returned will still be `Rohit Jain`
    builder =  null;
}

推荐阅读:


8
补充一点:仅返回值本身存储在栈中。如果您返回一个对象的引用,在finally块中对对象字段所做的修改仍将可见。 - Jason C
@JasonC。是的,没错。我会在答案中加上。谢谢:) - Rohit Jain
如果有人对为什么 builder = null 不会使返回值为空感到困惑,那是因为 传值 的原因。 - keyser
@Keyser。是的,就是那样。 - Rohit Jain

12

finally块被执行了。局部变量被增加了,但是该局部变量的值已经被复制为返回值。

根据Java语言规范,14.17:返回语句

带有表达式的返回语句试图将控制权转移给包含它的方法的调用者;表达式的值成为方法调用的值。

...

前面的描述说“试图转移控制权”,而不是只是“转移控制权”,因为如果方法或构造函数中存在任何try语句(§14.20),其try块或catch子句包含返回语句,则将执行这些try语句的任何finally子句,按照内部到外部的顺序执行,然后才将控制权转移到方法或构造函数的调用者。 finally子句的突然完成可能会干扰由返回语句启动的控制转移


3
好吧,我想肯定有某个人必须要真正阅读文档。每个聚会都有一个扫兴者。 - Hot Licks

0

在退出 try 前,您正在返回 x。我会这样做:

public class TryCatchTest {
    public static void main(String[] args) {
        System.out.println(test());
    }
    static int test() {
        int x = 1;
        try {
            do something with x.
        } finally {
            do something that will happen even in case of error;
            x = x + 1;
            return x;
        }
    }
}

1
我认为这样做没有抓住重点。 - Hot Licks

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接