运算符优先级
i++和++i的区别
- i++, 先用i的值参与计算,然后再自增1。如:
i=4;
a=i++; //此时,先将i的值赋给a,i再自增,故a=4,i=5 - ++i,先自增1,再参与计算。如:
i=4;
a=++i; //此时,i先自增1,再将值赋给a,故a=5,i=5
例题
public class AutoIncreaseTest{
public static void main(String[] args){
int i=0;
for( ; i<10; i++){
System.out.print(i + " "); //输出: 0 1 2 3 4 5 6 7 8 9
}
System.out.println();
System.out.println("After For: " + i); //输出: After For: 10
int j=0;
for( ; j<10; ++j){
System.out.print(j + " "); //输出: 0 1 2 3 4 5 6 7 8 9
}
System.out.println();
System.out.println("After For: " + j); //输出: After For: 10
int a = 0;
a = a ++;
System.out.println("a = " + a); //输出: a = 0
int b = 0;
b = ++ b; //IDE提示: The assignment to variable b has no effect
System.out.println("b = " + b); //输出: b = 1
int c = 0;
int d = 0;
int e = 0;
for (int k = 0; k < 99; k++) {
c = c ++;
e = d ++;
}
System.out.println("c = " + c); //输出: c = 0
System.out.println("d = " + d); //输出: d = 99
System.out.println("e = " + e); //输出: e = 98
int f = 0;
int g = 0;
int h = 0;
for (int k = 0; k < 99; k++) {
f = ++ f; //IDE提示: The assignment to variable f has no effect
h = ++ g;
}
System.out.println("f = " + f); //输出: f = 99
System.out.println("g = " + g); //输出: g = 99
System.out.println("h = " + h); //输出: h = 99
}
}
i++ 和 ++i的实现原理
public class Test {
public void testIPlus() {
int i = 0;
int j = i++;
}
public void testPlusI() {
int i = 0;
int j = ++i;
}
}
将上面的源代码编译之后,使用“ javap -c Test ”命令查看编译生成的代码(忽略次要代码)如下:
...
{
...
public void testIPlus();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=3, args_size=1
0: iconst_0 // 生成整数0
1: istore_1 // 将整数0赋值给1号存储单元(即变量i)
2: iload_1 // 将1号存储单元的值加载到数据栈(此时 i=0,栈顶值为0)
3: iinc 1, 1 // 1号存储单元的值+1(此时 i=1)
6: istore_2 // 将数据栈顶的值(0)取出来赋值给2号存储单元(即变量j,此时i=1,j=0)
7: return // 返回时:i=1,j=0
LineNumberTable:
line 4: 0
line 5: 2
line 6: 7
public void testPlusI();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=3, args_size=1
0: iconst_0 // 生成整数0
1: istore_1 // 将整数0赋值给1号存储单元(即变量i)
2: iinc 1, 1 // 1号存储单元的值+1(此时 i=1)
5: iload_1 // 将1号存储单元的值加载到数据栈(此时 i=1,栈顶值为1)
6: istore_2 // 将数据栈顶的值(1)取出来赋值给2号存储单元(即变量j,此时i=1,j=1)
7: return // 返回时:i=1,j=1
LineNumberTable:
line 9: 0
line 10: 2
line 11: 7
}
...
i=i++ 和 i=++i的实现原理
i=i++
int i = 0;
i = i++;
System.out.println("i=" + i); // 输出 i=0
使用javap查看编译生成的代码如下:
0: iconst_0 // 生成整数0
1: istore_1 // 将整数0赋值给1号存储单元(即变量i,i=0)
2: iload_1 // 将1号存储单元的值加载到数据栈(此时 i=0,栈顶值为0)
3: iinc 1, 1 // 1号存储单元的值+1(此时 i=1)
6: istore_1 // 将数据栈顶的值(0)取出来赋值给1号存储单元(即变量i,此时i=0)
7: getstatic #16 // 下面是打印到控制台指令
10: new #22
13: dup
14: ldc #24
16: invokespecial #26
19: iload_1
20: invokevirtual #29
23: invokevirtual #33
26: invokevirtual #37
29: return
从编码指令可以看出,i 被栈顶值所覆盖,导致最终 i 的值仍然是 i 的初始值。无论重复多少次 i = i++ 操作,最终 i 的值都是其初始值。可将其理解为:
- i++有三项操作:
将值赋给中间变量int temp=i;
i=i+1;
return i; - i=i++有四项操作:
将值赋给中间变量int temp=i;
i=i+1;
i=temp;
return i;
i = ++i
int i = 0;
i = ++i; // IDE提示“The assignment to variable i has no effect”警告
System.out.println("i=" + i); // 输出i=1
使用 i = ++i 得到了预期的结果,但同时IDE提示“The assignment to variable i has no effect”警告,警告的意思是将值赋给变量 i 毫无作用,并不会改变i的值。也就是说:i = ++i 等价于 ++i 。
++i 和 i++不是原子操作
虽然在Java中 ++i 是一条语句,字节码层面上也是对应 iinc 这条JVM指令,但是从最底层的CPU层面上来说,++i 操作大致可以分解为以下3个指令:
- 取数
- 累加
- 存储
其中的一条指令可以保证是原子操作,但是3条指令合在一起却不是,这就导致了 ++i 语句不是原子操作。
如果要保证累加操作的原子性,可以采取下面的方法(使用volatile修饰变量i也无法保证++i是原子操作):
- 将 ++i 置于同步块中,可以是synchronized或者J.U.C中的排他锁(如ReentrantLock等)。
- 使用原子性(Atomic)类替换 ++i ,具体使用哪个类由变量类型决定。如果 i 是整形,则使用AtomicInteger类,其中的AtomicInteger#addAndGet()就对应着 ++i 语句,不过它是原子性操作。