首先明确,笔者在这不讨论"+"号作为字符串拼接的作用,而是作为运算符产生的问题。
也就是说下在Java中 i+1 、 i++ 、 ++i之间的区别
含义
i+1
i++ 和 ++i 明显区别于 i+1 的是前两者的i的值都增加了。
如果给 i+1 赋值的话,又存在两种情况:
- i = i+1;
这个过程的结果相当于 ++i,i 在这次运算中增加了一个数。 - j = i+1;
在这个运算里 i 就相当于常量存在,自身不变。
int x=0;
int y=0;
while(x<10){
x = x+1;
System.out.print(x+" ");
}
System.out.println();
while(y<10){
++y;
System.out.print(y+" ");
}
复制代码
运算结果:
1 2 3 4 5 6 7 8 9 10
1 2 3 4 5 6 7 8 9 10
复制代码
在循环中来说,上面第一种情况的返回值的大小等价于 ++i ;第二种情况中 i 是不变的。
i++ 与 ++i
这两者要放到一起讨论。
通俗来讲,其中 i++ 是先赋值后运算; ++i 是先运算后赋值,但他们在Java中都只能作为右值使用,不能作为左值。
左值是对应(refer to)内存中有确定存储地址的对象的表达式的值,而右值是所有不是左值的表达式的值。
这两个在循环中的代码:
int i = 0;
int j = 0;
while(i<6){
int x = i++;
System.out.print(x+""+i+" ");
}
System.out.println();
while(j<6){
int y = ++j;
System.out.print(y+""+j+" ");
}
复制代码
结果:
01 12 23 34 45 56
11 22 33 44 55 66
复制代码
他们在寄存器中的运算过程用代码表示如下:
i++ >>>
0: iconst_0
1: istore_1
2: iload_1
3: iinc 1, 1
6: istore_2
7: return
++i >>>
0: iconst_0
1: istore_1
2: iinc 1, 1
5: iload_1
6: istore_2
7: return
复制代码
也就是说i++ 运算中,栈顶值是 0 ,++i 运算中栈顶值是 1。
总的来说,在 i++ 与 ++i 这两个运算中i都是增加了的,只是 i++ 返回的值是增加前的值, ++i 返回的值是增加后的值
使用中的注意事项
1. 有一个骚操作 i = i++; while循环中出现这个基本就死循环了
int i = 0;
while (i < 2) {
i = i++;
System.out.println(i);
}
复制代码
结果就是一堆 0,你可以自己想想是为什么。
对了如果你的输出语句不换行控制台是打印不出消息的,因为结果长度超出限制了。
2.多线程中的并发问题
i++ 与 ++i 都并非线程安全的运算,因为这两者都不是原子操作。
到这有人可能会想,是否可以用volatile关键字修饰来保证线程安全呢?答案是不能。因为volatile只能保证可见性,不能保证原子性。
那么到底怎么在多线程中保证这两者的线程安全呢?有两个办法:
- 用java.util.concurrent.atomic.AtomicInteger原子类下面的方法来替换这两个操作,当然这是整形的原子类,其他基本类型也有各自的原子类。这些原子类能保证原子操作。
- 同步块。加锁synchronized或者使用排他锁。