首先明確,筆者在這不討論"+"號作為字串拼接的作用,而是作為運算子產生的問題。
也就是說下在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或者使用排他鎖。