直接上两个网上很经典的demo
例子1
int i = 1;
int j = i++;
System.out.println(i);
System.out.println(j);
答案:输出2和1
直接反编译class文件,得到前两行的java指令,分析如下
j=i++.gif
可以看出来,i++分为两iload和iinc,所以j=i++会先将i压入操作数栈,再对变量i自加,所以下一步操作从操作数栈取到的i是自加之前的。
例子2
int i = 1;
i = i++;
System.out.println(i);
答案:输出1
再次观察java指令
i=i++.gif
所以i变量并不是没有自加,而是变量自加后又被操作数栈中的原始i赋值了,所以i看起来没变化
例子3
接下来看看++i是什么样子
int i = 1;
i = ++i;
System.out.println(i);
输出2
再次观察java指令
i=++i.gif
可以看到,i=++i相当于iinc和iload,所以操作数栈中存储的是自增之后的i,这样下次操作用的就是自增之后的i。
于是乎,我得到了大学老师告知的结论:
i++,i在语句结束后自增,当前语句使用的是原始i
++i,先对i进行自增,当前语句使用的是自增之后的i
上面的结论得到了System.out.println(i++)和System.out.printlin(++i)的证实
但是却不严谨,如下面的例子
例子4
int i = 1;
int[] arr = new int[10];
arr[i++] = 10 - i;
System.out.println(arr[1]);
输出8
按照之前的理解, arr[i++] = 10 - i; 中的i应该是1,所以答案是arr[1]=10-1=9啊,截取该行代码的java指令如下
11: aload_2 #加载arr数组到操作数栈
12: iload_1 #加载i到操作数栈 (arr[i++] = arr[1])
13: iinc 1, 1 #i变量自增(i=2)
16: bipush 10 #加载10到操作数栈
18: iload_1 #加载i到操作数栈 (2)
19: isub #执行减法 10-2=10-2=8
20: iastore #arr[i]=8
可以看到, 当编译器遇到i++后,确实是先iload然后iinc,但是后面又遇到了10-i,这时再次从变量栈加载i(2)到操作数栈,所以10-i = 2;
所以结论再精确点,应为
当语句中只有一个i:
i++,i在语句结束后自增,当前语句使用的是原始i
++i,先对i进行自增,当前语句使用的是自增之后的i
当语句中有多个i:
i++,该表达式之前的i为原始i,该表达式为原始i,该表达式之后i为自增后的i
++i, 该表达式之前的i为原始i,该表达式为自增后的i,该表达式之后的i为自增后的i
验证下:
int i = 1;
int[] arr = new int[]{2, 4, 6, 8, 10};
int[] arr1 = new int[5];
arr1[i] = arr[i++] - i; //arr1[1]=arr[1]-2
输出 [0,2,0,0,0]
int i = 1;
int[] arr = new int[]{2, 4, 6, 8, 10};
int[] arr1 = new int[5];
arr1[i] = arr[++i] - i;// arr1[1]=arr[2]-2
输出 [0,4,0,0,0]
但是真是不建议在一个复杂的语句中使用i++或++i,给自己增加难度不说,也增加的以后的维护成本;看似很炫的代码换来的是后人的诅咒 ... ...
网友评论