Java如何处理IF语句和效率

Java如何处理IF语句和效率,java,if-statement,Java,If Statement,我只是好奇Java在if语句中是如何工作的。(注意:当我在下面说“组件”时,我指的是由语句检查的单个部分,例如a,b,c) 哪一个在计算方面更有效 if (a && b && c) { do stuff } 或 我之所以问这个问题,是因为Java在第一个版本中所做的事情很重要。它是检查语句中的每一项内容还是检查a,如果它是false,则取消检查语句的其余部分 如果是这种情况,那么将最有可能失败的组件作为语句中的第一个组件是有意义的 如果每次都检查整个语句,则更合

我只是好奇Java在
if
语句中是如何工作的。(注意:当我在下面说“组件”时,我指的是由语句检查的单个部分,例如
a
b
c

哪一个在计算方面更有效

if (a && b && c) { do stuff }

我之所以问这个问题,是因为Java在第一个版本中所做的事情很重要。它是检查语句中的每一项内容还是检查
a
,如果它是
false
,则取消检查语句的其余部分

如果是这种情况,那么将最有可能失败的组件作为语句中的第一个组件是有意义的

如果每次都检查整个语句,则更合理的做法是将组件分成一组不同的语句,如第二个示例中所示。

对于
(a&&b&&c)
,条件“短路”,即一旦一个语句出现故障,其余语句将不进行计算

if (a && b && c) { do stuff }
但是,除非获得
a
b
c
的成本很高,否则这可能是过早优化的情况

如果部分或全部都很贵,那么如果你真的这么做了

if(getA()&&getB()&&getC())…


然后,如果
getA()
失败,甚至不会调用其他方法。

这些代码片段完全等效,并生成完全相同的字节码:

0:  iload_1
1:  ifeq    20 //a == false (0)
4:  iload_2
5:  ifeq    20 //b == false (0)
8:  iload_3
9:  ifeq    20 //c == false (0)
12: //do stuff
20: return

虽然解释不同,但代码生成器生成相同的输出。在第一种情况下,由于延迟求值,如果前一项为
false
,则不会求值后续项。在第二种情况下,如果不满足条件,则运行时不会更深。

在Java中,
&&
|
保证短路:操作数从左到右求值,结果确定后立即停止求值

从:

&
运算符类似于
&
(§15.22.2),但仅当其左侧操作数的值为
true
时,才计算其右侧操作数(从左到右分组)

|
运算符类似于
|
(§15.22.2),但仅当其左侧操作数的值为false时才计算其右侧操作数。它在语法上是左关联的(它从左到右分组)


这意味着您的问题中的两个代码片段完全相同。

检查这类问题的最佳方法是查看

在这种情况下,您需要:

在运行时,首先计算左侧操作数表达式;如果结果的类型为布尔型,则应进行拆箱转换(§5.1.8);如果结果值为false,则条件and表达式的值为false,并且不计算右侧操作数表达式

if (a && b && c) { do stuff }
因此,否,在表达式
a&&b&&c
中,如果
a
求值为
false
,则不计算
b
c

请注意,这不是
if
语句行为的一部分,而是
&
运算符行为的一部分。您可以在别处使用表达式看到相同的效果:

// Still won't evaluate b and c if a is false...
boolean x = a && b && c;

(值得注意的是,当您想查阅规范时,语言的哪一部分会导致什么行为。)

在第一种情况下,如果
在第一次遇到
错误
情况(短路)时,if
的计算结果为
错误
。否则,您将无法执行以下操作:

if(a != null && a.toString().equals("b"))
如果第二部分也将被评估,则不获取
NullPointerException

if (a && b && c) { do stuff }
那有短路<如果
a
的计算结果为false,则代码>&&将短路,这意味着未完成其余检查

除此之外,我认为这两种方法没有区别。

这是:

if (condition is met) {
       //continute to next check
           if (condition is met) {
               //continue to next check
                    if (condition is met) {
    do stuff }
  }
}
与:
if(a&&b&&c){do stuff}
相同,但会占用大量空间。您实际上不必担心这个特定代码段的效率,因此使用第一个选项是最佳选择。你也可以这样做

if (a && b){
 if (condition is met) {
        do stuff }
}

&&和| |是短路逻辑运算符。这意味着在您的示例中,如果a为false,则表达式的其余部分将不会被计算

if (a && b && c) { do stuff }
如果需要非短路逻辑运算符,可以使用&和|


如果需要确认,可以查看Java语言规范的以下语句:

我很好奇编译器会如何处理这两种情况,因此我使用Java 1.6编译了以下代码:

public class IfTest
{
    public static void main(String[] args)
    {
        IfTest iffy = new IfTest();
        iffy.doit();
    }

    private void doit()
    {
        Random rand = new Random();
        boolean a = rand.nextBoolean();
        boolean b = rand.nextBoolean();
        boolean c = rand.nextBoolean();

        if (a && b && c)
        {
            System.out.println("block 1");
        }

        if (a)
        {
            if (b)
            {
                if (c)
                {
                    System.out.println("block 2");
                }
            }
        }
    }
}
…然后使用进行反编译。下面是反编译器从类文件生成的内容:

// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.kpdus.com/jad.html
// Decompiler options: packimports(3) 
// Source File Name:   IfTest.java

import java.util.Random;

public class IfTest
{

    public IfTest()
    {
    }

    public static void main(String args[])
    {
        IfTest iffy = new IfTest();
        iffy.doit();
    }

    private void doit()
    {
        Random rand = new Random();
        boolean a = rand.nextBoolean();
        boolean b = rand.nextBoolean();
        boolean c = rand.nextBoolean();
        if(a && b && c)
            System.out.println("block 1");
        if(a && b && c)
            System.out.println("block 2");
    }
}

我想你用哪种方式写并不重要。

你可以自己很容易地尝试一下。我想通过制作a、b和c函数,这些函数在被调用时会打印出来,对吧?@Howard:我是一个极力主张尝试的人。然而,我不认为运行一些代码(甚至查看字节码)是确定短路行为是否得到保证的好方法。对于这一点,Java语言规范是决定性的资源?true意味着还必须对b进行评估-这是和,而不是OR@Tudor:没错。true应与
|
一起使用。谢谢。很有帮助,我没有考虑过那种特殊情况。很好的例子。所以你基本上是说这不重要?(除了在代码可读性方面)@Chro:是的,在性能方面并不重要。但是,如果计算例如<代码> A/COD>是费时的,考虑把它推得更远(或者更深),这样它就更有可能永远不会被评估。