11

考虑简单的例子

private static String isPositive(int val) {
    if (val > 0) {
        return "yes";
    } else {
        return "no";
    }
}

这里很简单: if val > 0return yeselse return no。但是在编译之后,在字节码中,这个 if 条件是相反的:

  private static isPositive(I)Ljava/lang/String;
   L0
    LINENUMBER 12 L0
    ILOAD 0
    IFLE L1
   L2
    LINENUMBER 13 L2
    LDC "yes"
    ARETURN
   L1
    LINENUMBER 15 L1
   FRAME SAME
    LDC "no"
    ARETURN

它检查:如果val <= 0则返回no,否则返回yes

首先,我认为<=检查更便宜,并且是某种优化。但是如果我将我的初始代码更改为

if (val <= 0) {
    return "no";
} else {
    return "yes";
}

它仍然会在字节码中反转:

   L0
    LINENUMBER 12 L0
    ILOAD 0
    IFGT L1
   L2
    LINENUMBER 13 L2
    LDC "no"
    ARETURN
   L1
    LINENUMBER 15 L1
   FRAME SAME
    LDC "yes"
    ARETURN

那么,有这种行为的原因吗?可以改成直截了当吗?

4

3 回答 3

8

可能是这样完成的,if以便在翻译的字节码中以相同的顺序显示两个代码块。

例如,这个 Java 代码:

if (val > 0) {
    return "yes";
} else {
    return "no";
}

翻译成这样的东西(伪代码):

If val <= 0, then branch to L1
return "yes"
L1:
return "no"  

请注意,在原始 Java 代码中,if检查条件以查看是否应该运行第一个代码块,而在翻译后的字节码中,检查是否应采用分支(跳过第一个代码块)。所以它需要检查一个互补的条件。

可以改成直截了当吗?

当然也可以保留条件,但是您需要颠倒两个代码块的顺序:

If val > 0, then branch to L1
return "no"
L1:
return "yes"  

不过,我不会说这个版本比前一个版本“更直接”。

无论如何,你为什么要改变它?两个版本都应该没问题。

于 2017-03-02T18:08:11.060 回答
5

实际上,反转条件是可能的最直接的编译策略。您编写与模式匹配的 Java 代码

if(condition_fullfilled) {
    somecode
}

它将被编译为与模式匹配的字节码

  goto_if_condition_not_fullfilled A_LABEL
  compiled_somecode
A_LABEL:

由于条件分支说明何时跳过条件代码,它的条件必须与您的源代码相反,即何时执行条件代码。

上面的例子,没有else一部分,演示了为什么没有简单的方法来编译if条件分支指令,条件分支指令与源代码具有相同的条件。这是可能的,但需要不止一个分支指令。

这种直接的if语句编译策略else也可以轻松扩展以处理elseelse当存在子句时,没有理由改变策略,例如切换语句的顺序。

请注意,在您的情况下,两个分支都以return语句结尾,之间没有区别

if (val > 0) {
    return "yes";
} else {
    return "no";
}

if (val > 0) {
    return "yes";
}
return "no";

反正。

于 2017-03-02T18:52:06.603 回答
0

原因可能是字节码用来执行其语句的操作数堆栈。

首先执行比较并将 1、0 或 -1 压入操作数堆栈。接下来,根据操作数堆栈上的值是否大于、小于或等于零来执行分支。

JavaCodeToByteCode#conditionals中有一个很好的图形细节 和一个详细的分支指令

于 2017-03-02T18:16:00.860 回答