对于表达式A && B || C && D, 一般我们认为可能有两种执行顺序(A && B)|| (C && D), 即先执行A && B 的判断,然后执行C && D的判断,最后再执行||的判断(当然这是没有考虑Java中"短路"的一般执行顺序(后面结果的验证就是根据"短路"的特性))
另外一种可能就是((A && B) || C) && D, 即先执行A && B的判断,将结果作为一个参数再与C进行或运算,最后再与D进行与运算。
分析:
首先介绍下Java的短路特性。
对于&&,如果左边的参数为false的话,那么就不会再去判断右边参数的真假而直接返回false;
对于||,如果左边的参数为true的话,那么就不会再去判断右边参数的真假而直接返回true。
下面是一个简单的示例简单了解下短路的概念:
public class Testcc {
private static int counter = 0;
private final int id = counter++;
public boolean test(boolean a) {
if(a)
System.out.println("执行了" + id);
return a;
}
public static void main(String[] args) {
Testcc tt0 = new Testcc();
Testcc tt1 = new Testcc();
Testcc tt2 = new Testcc();
Testcc tt3 = new Testcc();
Testcc tt4 = new Testcc();
Testcc tt5 = new Testcc();
Testcc tt6 = new Testcc();
Testcc tt7 = new Testcc();
// A && B
System.out.println("***** test A && B *****");
System.out.println(tt0.test(true) && tt1.test(true));
System.out.println(tt2.test(false) && tt3.test(true));
// A || B
System.out.println("***** test A || B *****");
System.out.println(tt4.test(true) || tt5.test(true));
System.out.println(tt6.test(false) || tt7.test(true));
}
}
// 输出结果如下:
***** test A && B *****
执行了0
执行了1
true
false
***** test A || B *****
执行了4
true
执行了7
true
完全符合我们上面的说法。
下面接着我们的思路往下说:
public class Testaa {
public static void show() {
boolean a = true;
boolean b = true;
boolean c = true;
boolean d = false;
System.out.println(test(a)&&test(b)||test(c)&&test(d));
}
public static boolean test(boolean d) {
if (d) {
System.out.println("d");
}
return d;
}
public static void main(String[] args) {
show();
}
}
按照第一种可能,如果 A && B为true的话,那么C && D就不会再执行了,那就只会输出两个d和最终的结果true;
按照第二种可能,A && B 为true,那么将不必执行和C的或运算,但是和D的与运算会执行; 因为d为false, 所以仍是输出两个d, 但结果为false。
请看输出结果:
d
d
true
正好验证了我们第一个猜想。即按照(A && B)||(C && D)的顺序运算。
下面是将上面的文件使用javap进行反汇编之后的结果:
Compiled from "Testaa.java"
public class Testaa extends java.lang.Object{
public Testaa();
// ...
public static void show();
Code:
0: iconst_1
1: istore_0
2: iconst_1
3: istore_1
4: iconst_1
5: istore_2
6: iconst_0
7: istore_3
8: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream;
11: iload_0
12: invokestatic #3; //Method test:(Z)Z
15: ifeq 25
18: iload_1
19: invokestatic #3; //Method test:(Z)Z
22: ifne 39
25: iload_2
26: invokestatic #3; //Method test:(Z)Z
29: ifeq 43
32: iload_3
33: invokestatic #3; //Method test:(Z)Z
36: ifeq 43
39: iconst_1
40: goto 44
43: iconst_0
44: invokevirtual #4; //Method java/io/PrintStream.println:(Z)V
47: return
public static boolean test(boolean);
Code:
0: iload_0
1: ifeq 12
4: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream;
7: ldc #5; //String d
9: invokevirtual #6; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
12: iload_0
13: ireturn
public static void main(java.lang.String[]);
//...
}
//主要JVM指令介绍
ifeq: 当栈顶int型数值等于0时跳转
ifne: 当栈顶int型数值不等于0时跳转
下面是我个人的理解,请大家参考:
根据上面的结果可以发现:iconst_0表示false; iconst_1表示true;
我们从第15行(红色的行号)看起:
1 先判断第一个参数是否为false,
1.1 若为false, 直接跳转到25行;
1.1.1 判断第三个参数是否为false;
a) 若为false, 则跳转43行,取iconst_0(true)的值作为整个结果的返回值;
b) 若为true, 则去判断第四个参数;
1.1.1.1判断第四个参数是否为false;
a) fasle, 则跳转到第43行,然后同前面的a);
b) true, 取常量iconst_1(true)的值作为整个结果的返回值.
1.2 若为true, 接着去判断第二个参数是否为true(注意是ifne是不为0则跳转);
a) 若为true, 则跳转39行, 取iconst_1的值作为整个结果的返回值。
b) 若为false, 则去判断第三个参数, 接着就是上面说的了。
从这也可以看出他的执行就是第一种。
疑问:但是为什么会是这样的运算顺序,还需要进一步思考。
解答:这是和运算符的优先级和结合顺序有关。查看了Java运算符的特性可以发现,&&的优先级高于||,并且是左结合,所以才有了上面的结果。
下面附上Java运算符的介绍:
还有一个问题,boolean类型的变量是以整数类型存储的?
个人看法:对于一个程序员来说,编写出的代码在保证功能和效率性能的基础上应该尽可能的实现简洁清晰易懂,这样对于别人理解代码以及后期维护都很有利。
以上就是这个问题的所有内容,如果有说的不对的地方,恳请大家能够批评指正,谢谢。