RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 729377
Accepted
Andrew Kachalin
Andrew Kachalin
Asked:2020-10-11 17:48:16 +0000 UTC2020-10-11 17:48:16 +0000 UTC 2020-10-11 17:48:16 +0000 UTC

最终如何在java中工作?

  • 772

有一个代码:

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

public static final void main(String[] args) {
    System.out.println(f());
}

OUTPUT: 2

似乎不言而喻,编译器命中后return 1应该结束方法并输出一个,因为方法是逐行执行的。但是编译器的行为有点奇怪,而不是返回一个,它属于该部分finally.

问题:编译器如何真正“看到”代码?也许他认为这个街区try是来自街区的呼唤finally?像这样:

int finallyCompilator(){
int tryCompilator(); 
return 2; //Здесь код метода finally
}

tryCompilator(){
return 1;  //Здесь код метода try
}

但即便如此,编译器将如何“看到”catch 块(如果有的话)还不清楚?

java
  • 3 3 个回答
  • 10 Views

3 个回答

  • Voted
  1. Best Answer
    Komdosh
    2020-10-11T18:02:13Z2020-10-11T18:02:13Z

    一切都简单多了,return块正在被替换

    为了在实践中看到这一点,让我们编译你的代码bytecode并打开它,例如,通过Intellij IDEA并看到下图:

    public class Main {
        public Main() {
        }
    
        private static int f() {
            try {
                boolean var0 = true;
                return 2;
            } finally {
                ;
            }
        }
    
        public static final void main(String[] var0) {
            System.out.println(f());
        }
    }
    

    我们可以注意到,在编译块时returnfromtry被替换为returnfromfinally

    升级版:

    让我们让情况更有趣,编译一个这样的函数

    private static int f (){
        try {
            System.exit(0);
            return 1;
        }
        finally {
            System.out.println("smth");
            return 2;
        }
    }
    

    它bytecode会是这样的:

    private static int f() {
        try {
            System.exit(0);
            boolean var0 = true;
        } finally {
            System.out.println("smth");
            return 2;
        }
    }
    

    在这里我们看到他return只是简单地离开了块try,System.exit(0)自然他会比块工作更快地被抛出应用程序finally。

    • 13
  2. default locale
    2020-10-11T20:03:22Z2020-10-11T20:03:22Z

    简短的回答:finally编译器将之前的块复制return到每个块catch中。更多详情如下。

    为什么 finally 应该被调用return的问题在问题中得到了回答:如果 try 返回,是否最终执行?

    在实现方面,除了@Komdosh 的研究,我尝试在Java 虚拟机规范中寻找finally 编译要求。第 3.13 章描述了如何使用 JSR 和 RET 指令进行最终编译。说明和实现示例已经过时,但是,从描述中,您可以大致了解标准的 finally 编译机制:

    jsr 指令在跳转前将下一条指令的地址(在索引 7 处返回)压入操作数堆栈。作为跳转目标的 astore_2 指令将操作数堆栈上的地址存储到局部变量 2 中。finally 块的代码(在本例中为 aload_0 和 invokevirtual 指令)运行。假设该代码的执行正常完成,ret 指令从局部变量 2 中检索地址并在该地址处恢复执行。执行返回指令,tryFinally 正常返回。

    带有 finally 子句的 try 语句被编译为具有特殊的异常处理程序,它可以处理 try 语句中抛出的任何异常。如果 tryItOut 抛出异常,则在 tryFinally 的异常表中搜索适当的异常处理程序。找到了特殊处理程序,导致在索引 8 处继续执行。索引 8 处的 astore_1 指令将抛出的值存储到局部变量 1 中。下面的 jsr 指令对 finally 块的代码进行子例程调用。假设代码正常返回,索引 12 处的 aload_1 指令将抛出的值推回操作数堆栈,随后的 athrow 指令重新抛出该值。

    因此,try-catch-finally 块中 finally 的解释应该等同于以下内容:

    • 整个块被包装在自定义异常处理程序中,该处理程序将未处理的异常catch(如果有)存储在局部变量中,跳转/跳转(JSR)到 finally 块,然后抛出异常;
    • 在每次返回之前,都会跳转到 finally 块。

    该指令JSR以前用于保存 finally 块的指令。但自版本 51 (Java 7) 以来的类文件不支持该指令。JSR 文档说该指令在版本 6 之前用于支持finally

    在 Java SE 6 之前的 Java 编程语言编译器的 Oracle 实现中,在 finally 子句的实现中使用了 jsr 指令和 ret 指令。

    从某个版本开始,Sun 编译器,然后是 Oracle,不再使用跳转,而是开始复制和嵌入 finally 块,从而消除了对指令的需要。

    鉴于上述情况,以下代码块:

    try {
        if(isSomething()) {
             return foo();
        }
        return bar(); 
    } catch(FooException e) {
        handle(e);
    } finally {
        /** блок finally **/
    }
    

    ,编译器将转换为等效于以下内容的块:

    try {
        if(isSomething()) {
            T temp1 = foo();
            /* копия блока finally */
            return temp1;
        }
        T temp2 = bar();
        /* копия блока finally */
        return temp2; 
    } catch(FooException e) {
        handle(e);
        /* копия блока finally */
    } catch(Throwable t) {
        /* копия блока finally */
        throw t;
    }
    

    检查 javac 为 Oracle JDK 8 生成的字节码确认 finally 块被复制了多次(invokevirtual #10- 仅在 finally 块中调用的方法,完整代码):

    Code:
       0: aload_0
       1: invokevirtual #8                  // Method isSomething:()Z
       4: ifeq          17
       7: aload_0
       8: invokevirtual #9                  // Method foo:()I
      11: istore_1
      12: aload_0
      13: invokevirtual #10                 // Копия finally 1
      16: ireturn
      17: aload_0
      18: invokevirtual #11                 // Method bar:()I
      21: istore_1
      22: aload_0
      23: invokevirtual #10                 // Копия finally 2
      26: ireturn
      27: astore_1
      28: aload_0
      29: aload_1
      30: invokevirtual #13                 // Method handle:(LMain$FooException;)V
      33: aload_0
      34: invokevirtual #10                 // Копия finally 3
      37: ireturn
      38: astore_2
      39: aload_0
      40: invokevirtual #10                 // Копия finally 4
      43: ireturn
    

    作为优化的结果,编译器可以减少一些指令,但方法应该不会发生显着变化。

    再次值得注意的是,我在规范中没有发现编译器必须为 finally 生成的字节码有严格的要求。据我了解,编译器的另一个实现可以为此使用 goto 指令,尽管增益可以忽略不计。在现代 Java 版本中,finally 在字节码级别没有特殊结构。

    相关链接:

    • 在 Habrahabr 上为 JVM 编译 Try/Catch/Finally 。
    • 为什么 Java 编译器最终会复制 Blocks
    • 哪些 Java 编译器使用 jsr 指令,有什么用?
    • 9
  3. HasmikGaryaka
    2020-10-11T17:59:18Z2020-10-11T17:59:18Z

    finally 块总是被执行。例外

    try { System.exit(0); }
    

    “也许它将 try 块视为来自 finally 块的调用”?不,只是在try之后执行finally,除非发生异常。如果它发生了,然后抓住,然后最后。

    • 0

相关问题

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    Python 3.6 - 安装 MySQL (Windows)

    • 1 个回答
  • Marko Smith

    C++ 编写程序“计算单个岛屿”。填充一个二维数组 12x12 0 和 1

    • 2 个回答
  • Marko Smith

    返回指针的函数

    • 1 个回答
  • Marko Smith

    我使用 django 管理面板添加图像,但它没有显示

    • 1 个回答
  • Marko Smith

    这些条目是什么意思,它们的完整等效项是什么样的

    • 2 个回答
  • Marko Smith

    浏览器仍然缓存文件数据

    • 1 个回答
  • Marko Smith

    在 Excel VBA 中激活工作表的问题

    • 3 个回答
  • Marko Smith

    为什么内置类型中包含复数而小数不包含?

    • 2 个回答
  • Marko Smith

    获得唯一途径

    • 3 个回答
  • Marko Smith

    告诉我一个像幻灯片一样创建滚动的库

    • 1 个回答
  • Martin Hope
    Air 究竟是什么标识了网站访问者? 2020-11-03 15:49:20 +0000 UTC
  • Martin Hope
    Алексей Шиманский 如何以及通过什么方式来查找 Javascript 代码中的错误? 2020-08-03 00:21:37 +0000 UTC
  • Martin Hope
    Qwertiy 号码显示 9223372036854775807 2020-07-11 18:16:49 +0000 UTC
  • Martin Hope
    user216109 如何为黑客设下陷阱,或充分击退攻击? 2020-05-10 02:22:52 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    user207618 Codegolf——组合选择算法的实现 2020-10-23 18:46:29 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5