【问题标题】:Unreachable code compiler error [duplicate]无法访问的代码编译器错误[重复]
【发布时间】:2012-02-14 11:53:53
【问题描述】:

以下代码给出unreachable statement 编译器错误

public static void main(String[] args) {
    return;
    System.out.println("unreachable");
}

有时出于测试目的,想要阻止调用某个方法,因此一种快速的方法(而不是在使用它的任何地方都将其注释掉)是立即从该方法返回,以便该方法不执行任何操作。然后我总是做围绕编译器错误是这样的

public static void main(String[] args) {
    if (true) {
        return;
    }
    System.out.println("unreachable");
}

我只是好奇,为什么是编译器错误?它会以某种方式破坏 Java 字节码,是保护程序员还是别的什么?

另外(这对我来说更有趣),如果将 java 编译为字节码进行任何类型的优化(或者即使它没有),那么为什么它不会在第二个示例中检测到公然无法访问的代码?编译器伪代码将用于检查语句是否不可访问?

【问题讨论】:

  • 您可能想在此页面阅读第 14.16 节:java.sun.com/docs/books/jls/second_edition/html/…
  • 感谢您的链接。我想我陷入了“汇编程序”的心态,因为我很难掌握由方法调用、if 语句和return 引起的“跳转”如何让无法到达的行在第一时间被检测到sn-p 但不是在第二个。
  • 使用 if(true) 的好方法....

标签: java compiler-construction


【解决方案1】:

无法访问的代码没有意义,因此编译时错误很有帮助。在第二个示例中无法检测到它的原因是,正如您所期望的,用于测试/调试目的。规范中对此进行了解释:

if (false) { x=3; }

不会导致编译时错误。优化编译器可能 意识到语句 x=3;永远不会被执行并且可以选择 从生成的类文件中省略该语句的代码,但是 语句 x=3;在技​​术上不被视为“无法到达” 此处指定的意义。

这种不同处理的基本原理是允许程序员 定义“标志变量”,例如:

static final boolean DEBUG = false;

然后写代码如:

if (DEBUG) { x=3; }

这个想法是应该可以改变 DEBUG 的值 从假到真或从真到假然后编译代码 正确,无需对程序文本进行其他更改。

参考:http://docs.oracle.com/javase/specs/jls/se8/html/jls-14.html#jls-14.21

【讨论】:

  • 有趣的是(从您的参考资料中)只有if 语句被视为测试无法访问代码的特殊情况。我可以理解if(DEBUG) 必须例外,但为什么if(true) 也例外?我想这样更容易。谢谢。
  • if (DEBUG)if (true) 完全相同。 DEBUG 是一个常量变量,因为它是最终变量,并使用编译时常量表达式进行初始化。 true 是原始类型的文字,因此也是一个常量表达式。即使可以区分两者,也只会造成混淆。人们会问:为什么if (DEBUG) 而不是 if (true)
【解决方案2】:

这是因为编译器编写者认为控件中的人是愚蠢的,并且可能并不是要添加永远不会执行的代码 - 所以通过抛出错误,它试图防止您无意中创建代码路径无法执行 - 而是迫使您做出决定(尽管,正如您已经证明的那样,您仍然可以解决它)。

【讨论】:

  • +1:保护你不做明显错误的事情。
  • 但如果只是快速测试,可能不会错。
【解决方案3】:

这个错误主要是为了防止程序员错误(两行或更多的交换)。在第二个 sn-p 中,您明确表示您不关心 system.out.println()。

【讨论】:

  • 但是第二个sn-p不会也是一个错误吗?例如,如果无意中离开。
  • 是的,它可以,但通常程序员这样做是为了禁用/启用某些功能,这比之前返回要明显得多。
【解决方案4】:

它会以某种方式破坏 Java 字节码,是为了保护程序员还是别的什么?

就 Java/JVM 而言,这不是必需的。此编译错误的唯一目的是避免愚蠢的程序员错误。考虑以下 JavaScript 代码:

function f() {
    return 
        {
            answer: 42
        }
}

此函数返回undefined,因为JavaScript 引擎在行尾添加分号并忽略死代码(如它所想)。 Java 编译器更聪明,当它发现你正在做的事情很明显并且明显错误时,它不会让你这样做。地球上没有办法你打算拥有死代码。这在某种程度上符合作为一种安全语言的 Java 前提。

【讨论】:

  • 那为什么第二个sn-p不会报错呢?我不认为它会更难被发现(但我可能错了)。
  • 哦,但它被检测到了。如果你想证明,用 if(true) 编译你的代码,然后用jad 反编译它,你会看到编译器删除了死代码
【解决方案5】:

http://docs.oracle.com/javase/specs/jls/se8/html/jls-14.html#jls-14.21

说:

14.21。无法访问的语句

如果由于无法访问而无法执行语句,则为编译时错误。

【讨论】:

    【解决方案6】:

    示例 1:

    在这种情况下,您会在任何语句之前返回,因为编译器永远不会执行该代码。

    public static void main(String[] args) {
    
        return;
        System.out.println("unreachable");
    }
    

    在第二个代码中,我已经把上面的 return 声明和它现在的工作 :)

    示例 2:

        public static void main(String[] args) {
    
        System.out.println("unreachable"); // Put the statement before return
        return;
    }
    

    这背后的原因是,如果您返回某个时间,那么之后的代码将永远不会执行,因为您已经返回了函数数据,因此它显示为无法访问的代码。

    【讨论】:

      【解决方案7】:

      这是因为它即使在那里也是浪费资源。此外,编译器设计者不想假设他们可以删除什么,而是宁愿强制您删除使其无法访问的代码或无法访问的代码本身。他们不知道那里应该有什么。在将代码编译为机器代码时将代码调整为更高效的优化与公然删除“您不需要”的代码之间存在差异。

      【讨论】:

      • 这并不能解释为什么它是错误而不是警告。
      猜你喜欢
      • 1970-01-01
      • 2016-07-09
      • 1970-01-01
      • 2021-05-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多