【问题标题】:Java's return value in try-catch-finally mechanismJava在try-catch-finally机制中的返回值
【发布时间】:2012-02-10 11:06:21
【问题描述】:

我刚刚遇到以下代码:

public class TestFinally {
    public static void main(String[] args) {
        int returnValue = function();

        System.out.println("Return value: " + returnValue);
    }

    public static int function() {
        try {
            return 1;
        } catch (Exception e){
            return 2;
        } finally{
            return 3;
        }
    }
}

毫无疑问,运行此代码将产生“返回值:3”的输出。

但是,我很好奇:

  1. JVM 中的内部机制。有谁知道虚拟机是否真的通过覆盖第一个“return 1”来替换堆栈上的返回值?如果是这样,我在哪里可以找到这方面的更多信息。
  2. 我还没有在 finally 机制中找到用于返回的用途,该机制以这种方式使用并允许在已实现的 在JVM中。如果将此代码构造用作返回的手段 错误代码,我认为有更好的方法来记录错误 或返回这些错误代码。有没有人发现这样的用途 构造?

非常感谢。

干杯, 维恩

【问题讨论】:

  • to 1.: 不知道,我也想知道。 to 2.: finally 可以用来做认为绝对需要做的事情,例如关闭流。我认为异常比错误代码更好,因为您已经在方法签名中看到了可能出错的地方(无需阅读任何文档),并且对于编码人员来说更难忽略它们。
  • 嗯,我不能真正回答第一个问题,但第二个似乎很明显。 finally 子句用于清理(如关闭流),即使代码返回也必须进行清理。但是,在 finally 子句中返回无论如何都是不好的做法,只是不要这样做。
  • 我认为您不应该在 finnaly 块内更改返回值,但是为什么他们应该允许您这样做呢?但这样做不是一个好习惯。
  • 感谢大家的意见。是的,我意识到我发布的问题并不那么清楚。我已经编辑并澄清了这个问题。我很好奇是否有人见过这样的代码使用,即在 try 块中完成返回,在 finally 块中使用第二个返回来覆盖 try 块的返回值。有人见过这样的用途吗?

标签: java coding-style jvm


【解决方案1】:

我在 Java 语言规范中找到的至少定义了你的代码 sn-p 应该返回 3。当然,它没有提到 JVM 应该如何实现这一点,以及可以做哪些优化。

14.20.2 部分定义了

如果 try 块的执行由于任何其他原因 R 突然完成,则执行 finally 块。然后有一个选择:

  1. 如果 finally 块正常完成,则由于原因 R,try 语句会突然完成。
  2. 如果 finally 块由于原因 S 突然完成,则 try 语句由于原因 S 突然完成(并且原因 R 被丢弃)。

第 14 章的开头(更准确地说是section 14.1)指定了正常和突然完成是什么。例如,具有给定值的return 是突然完成。

因此在这种情况下,finally 块会突然完成(原因:return 具有给定值),因此try 将出于相同原因突然完成(并返回 3)。 section 14.17 about the return statement 也证实了这一点

如果表达式的计算正常完成,产生一个值 V,那么 return 语句突然完成,原因是一个返回值 V。

【讨论】:

    【解决方案2】:

    FWIW,我收到有关功能的警告:

    public static int function(){
        try{
            return 1;
        }catch(Exception e){
            return 2;
        }finally{
            return 3; //WARNING on this line
        }
    }
    

    即。它告诉我“最终块无法正常完成”。无论如何,我仍然得到 3 作为返回值。

    无论如何,如果我尝试另一个示例:

    public class TestFinally {
        public static void main(String[] args) {
            int returnValue = function();
    
            System.out.println("Return value: " + returnValue);
        }
    
        public static int function() {
            try {  
    
                return 1;  
                }  
            catch (Exception e) {   
                return 2;  
                }  
            finally {  
                System.out.println("i don't know if this will get printed out.");
            }
        }
    }
    

    输出将是(显然)

    i don't know if this will get printed out.
    Return value: 1
    

    我不知道 JVM 是如何实现它的,但查看它的最简单方法(至少在概念上)是:

    1. “try”中的返回值被压入栈中,
    2. 然后执行“finally”块,
    3. 新的返回值被压入堆栈
    4. 函数退出,返回值从堆栈中弹出,因此忽略第一个。

    确实是非常巧妙的问题。

    【讨论】:

      【解决方案3】:

      实现由JVM决定,JVM有很多。您可以深入了解OpenJDK's source code 以了解它如何实现finally,但这不是唯一的方法。就语言而言,重要的是行为。

      我不明白第 2 点——为什么finally 存在?它不像你建议的那样只是一种返回错误代码的方法。您根本不必从finally 内部返回。该结构的存在是为了确保在某段代码之后运行某种清理代码,无论它如何终止,无论是正常终止还是通过异常或返回。

      【讨论】:

        【解决方案4】:

        完整解释页面:439=> http://docs.oracle.com/javase/specs/jls/se8/jls8.pdf

        如果表达式的评估正常完成,则生成 值V,则return语句突然完成,原因是return 值为 V。

        前面的描述说的是“试图转移控制权”,而不仅仅是“转移 控制”,因为如果方法或构造函数中有任何 try 语句(第 14.20 节) 其 try 块或 catch 子句包含 return 语句,然后是任何 finally 这些 try 语句的子句将按从内到外的顺序执行,然后 控制权转移到方法或构造函数的调用者。 突然完成一个 finally 子句 可以中断由 return 语句启动的控制转移。

        【讨论】:

          猜你喜欢
          • 2021-05-30
          • 2014-04-27
          • 2018-11-05
          • 2011-11-04
          • 2011-10-31
          • 2012-05-20
          • 2011-11-17
          • 2014-12-26
          • 2016-03-28
          相关资源
          最近更新 更多