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)。

这导致:

  1. 开始评估+=
  2. 评估赋值给variablesa左侧。
  3. 评估variablesa为将在添加中使用的值3
  4. 开始评估*
  5. 评估第一个a++ 。 这将返回3的当前值并将a设置为4
  6. 评估第二a++ 。 这将返回4的当前值并将a设置为5
  7. 计算产品:3 * 4 = 12
  8. 执行+= 。 第三步评估左侧为3 ,右侧为12 。 所以它将3 + 12 = 15分配给a
  9. 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