Java中的"+"运算问题

195 阅读3分钟

首先明确,笔者在这不讨论"+"号作为字符串拼接的作用,而是作为运算符产生的问题。
也就是说下在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只能保证可见性,不能保证原子性。
那么到底怎么在多线程中保证这两者的线程安全呢?有两个办法:

  1. 用java.util.concurrent.atomic.AtomicInteger原子类下面的方法来替换这两个操作,当然这是整形的原子类,其他基本类型也有各自的原子类。这些原子类能保证原子操作。
  2. 同步块。加锁synchronized或者使用排他锁。

ok,到此结束