a =(a ++)*(a ++)在Java中给出奇怪的结果
我正在学习OCPJP考试,所以我必须了解Java的每一个奇怪的细节。 这包括前后增量操作符应用于variables的顺序。 下面的代码给了我奇怪的结果:
int a = 3; a = (a++) * (a++); System.out.println(a); // 12 答案不应该是11吗? 或者,也许13? 但不是12!
跟进:
以下代码的结果是什么?
 int a = 3; a += (a++) * (a++); System.out.println(a); 
	
 第a++ 之后变成4.所以你有3 * 4 = 12 。 
  ( a在第二个a++之后变成5,但被丢弃,因为赋值a =覆盖它) 
您的声明:
 a += (a++) * (a++); 
相当于其中的任何一个:
 a = a*a + 2*a a = a*(a+2) a += a*(a+1) 
改用其中的任何一种。
  a++意味着“a的值,然后a增加1”。 所以当你跑步 
 (a++) * (a++) 
 首先对a++进行评估,然后生成值3.然后将a++加1,然后对第二个a++进行评估。  a产生4的值,然后再次递增(但是现在不重要) 
所以这变成了
 a = 3 * 4 
等于12。
 int a = 3; a += (a++) * (a++); 
首先构build语法树:
 += a * a++ a++ 
评估它从recursion的最外层元素和下降开始。 对于每个元素做:
- 从左到右评估孩子
- 评估元素本身
  +=运算符是特殊的:它被扩展为left = left + right ,但只评估expression式left一次。 在右边被评估为一个值之前,仍然左边被评估为一个值(而不仅仅是一个variables)。 
这导致:
-  开始评估+=
-  评估赋值给variablesa左侧。
-  评估variablesa为将在添加中使用的值3。
-  开始评估*
-  评估第一个a++。 这将返回3的当前值并将a设置为4
-  评估第二a++。 这将返回4的当前值并将a设置为5
- 计算产品:3 * 4 = 12
-  执行+=。 第三步评估左侧为3,右侧为12。 所以它将3 + 12 = 15分配给a。
- a的最终值是15。
这里要注意的一点是,运算符优先级对评估顺序没有直接的影响。 它只影响树的forms,从而间接地影响秩序。 但是在树中的兄弟姐妹中,评估总是从左到右,而不pipe操作符的优先级如何。
  (a++)是后增量,所以expression式的值是3。 
  (a++)是后增量,所以expression式的值现在是4。 
expression评估从左到右发生。
 3 * 4 = 12 
每次你使用++时,你都是后增加一个。 这意味着第一个a ++评估为3,第二个评估为4.3 * 4 = 12。
对于运营商如何运作普遍缺乏了解。 老实说,每个操作员都是语法糖。
所有你需要做的就是了解每个操作员背后究竟发生了什么。 假设如下:
 a = b -> Operators.set(a, b) //don't forget this returns b a + b -> Operators.add(a, b) a - b -> Operators.subtract(a, b) a * b -> Operators.multiply(a, b) a / b -> Operators.divide(a, b) 
复合运算符可以使用这些泛化来重写(为简单起见,请忽略返回types):
 Operators.addTo(a, b) { //a += b return Operators.set(a, Operators.add(a, b)); } Operators.preIncrement(a) { //++a return Operators.addTo(a, 1); } Operators.postIncrement(a) { //a++ Operators.set(b, a); Operators.addTo(a, 1); return b; } 
你可以重写你的例子:
 int a = 3; a = (a++) * (a++); 
如
 Operators.set(a, 3) Operators.set(a, Operators.multiply(Operators.postIncrement(a), Operators.postIncrement(a))); 
哪些可以使用多个variables分割出来:
 Operators.set(a, 3) Operators.set(b, Operators.postIncrement(a)) Operators.set(c, Operators.postIncrement(a)) Operators.set(a, Operators.multiply(b, c)) 
这当然更加冗长,但是显而易见,你永远不想在一行上执行两个以上的操作。
的情况下 :
 int a = 3; a = (a++) * (a++); a = 3 * a++; now a is 4 because of post increment a = 3 * 4; now a is 5 because of second post increment a = 12; value of 5 is overwritten with 3*4 ie 12 
因此我们得到12的输出。
的情况下 :
 a += (a++) * (a++); a = a + (a++) * (a++); a = 3 + (a++) * (a++); // a is 3 a = 3 + 3 * (a++); //a is 4 a = 3 + 3 * 4; //a is 5 a = 15 
这里需要注意的要点是,在这种情况下,编译器从左到右求解,在后递增的情况下,在计算中使用增量之前的值,并且从左向右移动递增值。
  (a++)意味着返回a并增加,所以(a++) * (a++)意味着3 * 4 
这里是java代码:
 int a = 3; a = (a++)*(a++); 
这是字节码:
  0 iconst_3 1 istore_1 [a] 2 iload_1 [a] 3 iinc 1 1 [a] 6 iload_1 [a] 7 iinc 1 1 [a] 10 imul 11 istore_1 [a] 
这是发生了什么事情:
将3推入堆栈,然后从堆栈中popup3并将其存储在堆栈中。 现在a = 3,堆栈是空的。
  0 iconst_3 1 istore_1 a 
现在它将“a”(3)的值推入堆栈,然后递增(3 – > 4)。
  2 iload_1 [a] 3 iinc 1 1 [a] 
所以现在“a”等于“4”堆栈等于{3}。
然后再次加载“a”(4),推入堆栈并递增“a”。
  6 iload_1 [a] 7 iinc 1 1 [a] 
现在“a”等于5,堆栈等于{4,3}
所以它最终从堆栈(4和3)中popup两个值,并将其重新存储到堆栈(12)中。
  10 imul 
现在“a”等于5,堆栈等于12。
最后是从堆栈中popup12个,并存储在一个。
  11 istore_1 [a] 
TADA!
它是12.expression式开始从左边评估。 所以它确实:
 a = (3++) * (4++); 
一旦第一部分(3 ++)被评估,a就是4,所以在下一部分,它做a = 3 * 4 = 12。注意最后一个后增加(4 ++)被执行,但是没有任何影响因为在此之后a被赋值12。
例1
 int a = 3; a = (++a) * (a++); System.out.println(a); // 16 
例2
 int a = 3; a = (++a) * (++a); System.out.println(a); // 20 
 只是为了确定在哪里把++expression式改变基于位置的值。 
每个人都清楚地解释了第一个expression式,为什么a的值是12。
对于随后的问题,对于偶然的观察者来说,答案是完全明显的:
17
前缀和后缀前缀增量比乘法运算符具有更高的优先级。 因此expression式被评估为3 * 4。
如果您在下次使用时使用++,则会加1。 所以你这样做
 a = 3 * (3 + 1) = 3 * 4 = 12