Java 三元和If/Else是否编译为相同的东西,为什么?

Java 三元和If/Else是否编译为相同的东西,为什么?,java,Java,编译器是否将简单的三元语句编译成与编译简单的if-else语句相同的东西?还有,为什么编译器会被设计成以不同的方式编译它们 例如,这是否: int a = 169; int b = 420; int c; c = a > b ? 42:69; 编译为与此相同的内容: int a = 169; int b = 420; int c; if(a>b) c = 42; else c = 69; 这个问题不是关于哪一个更好或者什么时候使用每一个,所以请不要在您的答案中包含这一点。首先,这

编译器是否将简单的三元语句编译成与编译简单的if-else语句相同的东西?还有,为什么编译器会被设计成以不同的方式编译它们

例如,这是否:

int a = 169;
int b = 420;
int c;
c = a > b ? 42:69;
编译为与此相同的内容:

int a = 169;
int b = 420;
int c;
if(a>b) c = 42;
else c = 69;

这个问题不是关于哪一个更好或者什么时候使用每一个,所以请不要在您的答案中包含这一点。

首先,这取决于实现。JLS并没有明确规定特定代码段或操作必须如何编译,只要字节码在支持Java虚拟机规范的VM上运行时满足Java语言规范。不同的编译器可以生成与给定示例不同的字节码,只要它在兼容JVM上运行时给出相同的结果

在Java8的
javac
(1.8.065)中,条件运算符和if-else的代码不同

三值运算符控制将哪个值推送到堆栈,然后无条件存储堆栈顶部的值。在这种情况下,如果
a>b
,则按下42,代码跳转到
istore
,否则按下59。然后,上面的任何值都是
istore
d到
c

在if-else中,实际调用
istore
指令的条件控件

但是请注意,在这两种情况下,指令都是“compare小于或等于”,它跳到else分支(否则继续if分支)

下面可以看到各种编译器生成的字节码。您可以使用OpenJDK JDK中提供的
javap
工具(示例命令行
javap-c ClassName
)自己获得它

带三值的javac:

  public static void main(java.lang.String...);
    Code:
       0: sipush        169
       3: istore_1
       4: sipush        420
       7: istore_2
       8: iload_1
       9: iload_2
      10: if_icmple     18
      13: bipush        42
      15: goto          20
      18: bipush        69
      20: istore_3
      21: return
带if-else的javac:

  public static void main(java.lang.String...);
    Code:
       0: sipush        169
       3: istore_1
       4: sipush        420
       7: istore_2
       8: iload_1
       9: iload_2
      10: if_icmple     19
      13: bipush        42
      15: istore_3
      16: goto          22
      19: bipush        69
      21: istore_3
      22: return
}
然而,使用ecj,代码就更奇怪了。三元运算符有条件地推送一个或另一个值,然后弹出该值以丢弃该值(不存储):

ecj
with if-else以某种方式优化了推送/存储,但仍然包括一个奇怪的比较(请注意,比较没有需要保留的副作用):


当我添加一个
System.out.println(c)
来抵消这个未使用的值丢弃时,我发现这两个语句的结构与
javac
的结构相似(三元组执行条件推送和固定存储,而if-else执行条件存储)。

无法在大范围内回答这个问题。java等VM语言将使用非常复杂的算法在运行时优化bytcode。请看

纯粹基于编译器的语言在某种程度上更具可预测性,但我们需要考虑语言、操作系统、编译器版本等的单独组合


相信你的编译器和/或虚拟机可以为你优化简单的东西。它可以进行更复杂的优化。

对于编译器,以下是一条带有三元表达式的语句:

c = a > b ? 42 : 69;

对于编译器,以下是三种不同的语句:

每个语句独立于其他语句编译成字节码

分析单独的语句以检测通用性,并重新排列代码以生成“更好”的字节码称为优化,这是完全可选的

大多数人编译时不进行优化,因为编译时优化与运行时优化相比效率很低,而且编译时优化会阻止(使)调试代码变得复杂,因为生成的代码将不再与源代码行号直接相关


示例:如果左侧改为
myObj.myField
,则如果
myObj
为null,则它可以生成
NullPointerException
。如果编译器重新排列代码,任何堆栈跟踪都无法判断是哪一行导致了异常。

没有人提到OpenJDK。。。您可以查看编译Java的代码(从JDK11开始,OpenJDK正在与OracleJDK合并,这就是为什么您最多只能查看JDK9的源代码)。以下是我在这篇文章中的简短回答: 我查看了JDK9的源代码,据我所知,它是OpenJDK编译器的最新源代码,无需支付许可证即可查看

Javac源代码实际上做什么 为了勇敢的人,为了勇敢的人,为了少数人 我决定看看javac的源代码。。。这花了一段时间,但在他们的《javac搭便车指南》的帮助下,我找到了一条确定发生了什么的路线。查看(第914行): 你看到了吗?让我澄清一下,第905-918行这样说:

    /** Expression1Rest = ["?" Expression ":" Expression1]
     */
    JCExpression term1Rest(JCExpression t) {
        if (token.kind == QUES) {
            int pos = token.pos;
            nextToken();
            JCExpression t1 = term();
            accept(COLON);
            JCExpression t2 = term1();
            return F.at(pos).Conditional(t, t1, t2);
        } else {
            return t;
        }
    }
注释告诉我们这是它们用于解析三元表达式的内容,如果我们查看它返回的内容,它将返回一个条件,其中
t
是要计算的表达式,
t1
是第一个分支,
t2
是第二个分支。让我们看看<代码>条件< /代码>。它看起来像是从
F
调用
Conditional
,如果我们再深入一点,就会发现是
TreeMaker
,你可能会问树生成器是什么?它是一个抽象语法树,通常用作正在解析的代码的中间表示(请在此处查看)。无论如何,如果我们查看该文件()的内部,我们可以在第306-313行看到:

    public JCConditional Conditional(JCExpression cond,
                                   JCExpression thenpart,
                                   JCExpression elsepart)
    {
        JCConditional tree = new JCConditional(cond, thenpart, elsepart);
        tree.pos = pos;
        return tree;
    }

这进一步证实了我们的想法,三元表达式的编译与if-else语句(也称为条件语句)完全相同:)我鼓励有兴趣的人看看《搭便车指南》和代码,事实上,很有意思的是,即使是商业级的编译器也会遵循你在大学标准编译器课程中学到的许多基本原理。

你知道如何查看生成的字节码吗?你是在问一些特殊的编译器吗?这两个片段在语义上是等价的
if (a > b) {  // statement 1
    c = 42;   // statement 2
} else {
    c = 69;   // statement 3
}
    /** Expression1Rest = ["?" Expression ":" Expression1]
     */
    JCExpression term1Rest(JCExpression t) {
        if (token.kind == QUES) {
            int pos = token.pos;
            nextToken();
            JCExpression t1 = term();
            accept(COLON);
            JCExpression t2 = term1();
            return F.at(pos).Conditional(t, t1, t2);
        } else {
            return t;
        }
    }
    public JCConditional Conditional(JCExpression cond,
                                   JCExpression thenpart,
                                   JCExpression elsepart)
    {
        JCConditional tree = new JCConditional(cond, thenpart, elsepart);
        tree.pos = pos;
        return tree;
    }