对于表达式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类型的变量是以整数类型存储的?
个人看法:对于一个程序员来说,编写出的代码在保证功能和效率性能的基础上应该尽可能的实现简洁清晰易懂,这样对于别人理解代码以及后期维护都很有利。
以上就是这个问题的所有内容,如果有说的不对的地方,恳请大家能够批评指正,谢谢。
相关推荐
相对应的还有前缀表达式(Prefix Notation),如:"+ - A * B C D",转换成中缀表达式为:"A - B * C + D";后缀表达式 (Postfix Notation),比如前所述的中缀表达式转换为后缀表达式为:"A B C * - D +"。 四、...
比较详实 第三章: C语言程序设计初步 C语言程序设计 本课介绍C语言程序设计的基本方法...&是一个取地址运算符,&a是一个表达式,其功能是求变量的地址。 void main(){ int a,b,c; printf("input a,b,c\n"); scanf...
在一个C++程序中,main函数的位置( c )。 (a) 必须在程序的开头 (b) 必须在程序的后面 ( c ) 可以在程序的任何地方 (d) 必须在其它函数中间 2.用C++语言编制的源程序要变为目标程序必须要经过( d )。 (a) ...
“|”运算符和“&”运算符分别对变量a与b各个对应位的运算得到了变量c和变量d的值。对变量e和f的赋值说明了“^”运算符的功能。字符串数组binary 代表了0到15 对应的二进制的值。在本例中,数组各元素的排列顺序显示...
(15) 在软件生命周期中,能准确地确定软件系统必须做什么和必须具备哪些功能的阶段是(D) 注:即第一个阶段 A. 概要设计 B. 详细设计 C. 可行性分析 D. 需求分析 (16) 数据流图用于抽象描述一个软件的逻辑模型,数据...
(28) 一个类可以从直接或间接的祖先中继承所有属性和方法。采用这个方法提高了软件的______。 答:可重用性 (29) 面向对象的模型中,最基本的概念是对象和 ______。 答:类 (30) 软件维护活动包括以下几类:改正性...
练习题 1. 栈和队列的共同点是__C__ A. 都是先进先出 B .... 表达式 a*(b+c)-d 的后缀表达式是_abc+*b--___ 7.经过以下队列运算后,队头的值是_b__. InitQueue(qu);enQueue(qu,a); enQueue(qu,b);enQueue(qu,c)
1.6 写一个程序,输入a,b,c三个值,输出其中最大者。 1 第2章 程序的灵魂——算法 2 2.1 什么叫结构化的算法?为什么要提倡结构化的算法? 2 2.7 什么叫结构化程序设计?它的主要内容是什么? 2 第3章 数据类型、...
A 输入、处理和输出结构 B 常量、变量和表达式结构 C 表达式、语句和函数结构 D 顺序、选择和循环结构 4. 在C语言中,决定int数的表示范围的因素是( A )。 A int类型占用的字节数量 B 人为事先的约定和习惯 C 所...
( BCDE ) A.RAM B.ROM C.硬盘 D.软盘 E.光盘 2.设有说明:int u=1,v=5;则下列表达式的值为1的有( AE ) A.u&v B.v>>2 C.u^u D.u¦v E.(u)/5 23.下列循环将会产生死循环的有(ACDE ) A.while(1)...
( BCDE ) A.RAM B.ROM C。硬盘 D。软盘 E.光盘 2、设有说明:int u=1,v=5;则下列表达式得值为1得有( AE ) A.u&v B.v〉〉2 C.u^u D.u¦v E。(u〈)/5 23。下列循环将会产生死循环得有(ACDE ) A。...
3.4 有这样一个巧妙的表达式:a^= b^= a^= b; 它不需要临时变量就可以交换a和b的值。 3.5 可否用显式括号来强制执行我所需要的计算顺序并控制相关的副作用?就算括号不行,操作符优先级是否能够控制计算顺序呢? 3.6...
*3.4 有这样一个巧妙的表达式:a^=b^=a^=b;它不需要临时变量就可以交换a和b的值。 3.5 可否用显式括号来强制执行我所需要的计算顺序并控制相关的副作用?就算括号不行,操作符优先级是否能够控制计算顺序呢? ...
1解:源程序是指以某种程序设计语言所编写的程序。目标程序是指编译程序(或解释...逗号在C语言中被视为分隔符和运算符,作为优先级最低的运算符,运算结果为逗号表达式最右侧子表达式的值(如:(a,b,c,d)的值为d)。
运算表达式 9.___D___就是一种只能进行wait操作与signal操作的特殊变量 A. 调度 B. 进程 C. 同步 D. 信号量 10.分配给进程占用处理机的时间到而强迫进程P让出处理器,或有更高优先级的进程 要运行,迫使正在运行的进程...
*3.4 有这样一个巧妙的表达式:a^= b^= a^= b; 它不需要临时变量就可以交换a和b的值。 34 3.5 可否用显式括号来强制执行我所需要的计算顺序并控制相关的副作用?就算括号不行,操作符优先级是否能够控制计算顺序...