寫在前面:博主是一只經過實戰開發歷練後投身培訓事業的“小山豬”,暱稱取自動畫片《獅子王》中的“彭彭”,總是以樂觀、積極的心態對待周邊的事物。本人的技術路線從Java全棧工程師一路奔向大數據開發、數據挖掘領域,如今終有小成,願將昔日所獲與大家交流一二,希望對學習路上的你有所助益。同時,博主也想通過此次嘗試打造一個完善的技術圖書館,任何與文章技術點有關的異常、錯誤、注意事項均會在末尾列出,歡迎大家通過各種方式提供素材。
- 對於文章中出現的任何錯誤請大家批評指出,一定及時修改。
- 有任何想要討論和學習的問題可聯繫我:[email protected]。
- 發佈文章的風格因專欄而異,均自成體系,不足之處請大家指正。
Java中的位運算符
文本關鍵字:位運算符、位邏輯運算符、移位運算符、異或運算
文章目錄
一、位運算符
大家在接觸運算符的時候通常都已經學完了變量的使用,對於算術以及賦值運算的感覺就是So easy!這不就是小學的知識嘛,對於邏輯運算符的部分依然無壓力,這不就是中學的知識嘛?但是突然出現了一個位運算符,啥是移位?啥是異或?接下來就先從簡單的開始。
說起位運算符,其實就是基於數據存儲的二進制位進行的運算,更底層,所以效率更高。另外一個需要注意的問題就是:由於小數在進行存儲的時候採用的是IEEE(符號、指數、尾數)方式,並不止對整數和小數部分直接轉換爲二進制來存儲的,所以小數是不能使用位運算符來操作的。對於整數和字符型的運算符操作也有一些潛在的法則,相信看完這篇文章你很容易就會掌握。
二、邏輯運算
在邏輯運算中我們已經使用過能夠表達邏輯意義的運算符,如:&&,||,!。這些運算符都有一個共同點,那就是:運算符兩邊都是布爾值或布爾表達式,他們能夠操作的數據類型有限,只能夠幫我們進行邏輯運算。有些教材將&,|等位運算符也歸爲邏輯運算符,因爲按位與(&)、按位或(|)能夠操作的數據類型較多,其中就包括布爾類型,並且也能夠幫助我們進行邏輯運算,但是小編還是建議按照符號本身的運算方式和操作數據類型等來記憶。
1. 與(&)
- 與運算
與運算相當於物理電路中的串聯電路,我們假設用1代表通路,用0代表斷路,那麼對於串聯電路來說,只有當運算符兩邊全爲1(通路)時,運算結果才爲1(通路)。
- 按位與
那麼按位與就是將運算符兩邊的數字轉換爲二進制後,在每兩個對應位置上的數字進行與運算,再將最後的結果按十進制寫出就可以了。
24 & -30:
00000000 00000000 00000000 00011010 = 26
11111111 11111111 11111111 11100010 = -30
00000000 00000000 00000000 00000010 = 26 & -30 = 2
- 邏輯與的短路問題
當我們在使用邏輯與(&&)時會遇到一個短路問題:當用&&把多個布爾表達式連接起來的時候,爲了以最快的速度得出結果,那麼有些式子將不會執行,被跳過的式子中的代碼也就不會被執行。比如:(假式 && 真式 && 真式),經過前兩個式子的結果已經能夠確定整個式子的結果:爲假,第三個式子無論爲真或假都不會影響最終結果,這個時候就會進行跳過。
但是對於&(按位與),由於本質上是一個位運算,只不過同時也支持布爾類型的直接運算而已,所以不會出現表達式不執行的情況。
2. 或(|)
- 或運算
或運算相當於物理電路中的並聯電路,我們假設用1代表通路,用0代表斷路,那麼對於並聯電路來說,只要運算符兩邊有一個爲1(通路)時,運算結果就爲1(通路)。
- 按位或
那麼按位或就是將運算符兩邊的數字轉換爲二進制後,在每兩個對應位置上的數字進行或運算,再將最後的結果按十進制寫出就可以了。
26 | -30:
00000000 00000000 00000000 00011010 = 26
11111111 11111111 11111111 11100010 = -30
11111111 11111111 11111111 11111010 = 26 | -30 = -6
- 邏輯或的短路問題
當我們在使用邏輯或(||)時會遇到一個短路問題:當用||把多個布爾表達式連接起來的時候,爲了以最快的速度得出結果,那麼有些式子將不會執行,被跳過的式子中的代碼也就不會被執行。比如:(真式 || 假式 && 假式),經過前兩個式子的結果已經能夠確定整個式子的結果:爲真,第三個式子無論爲真或假都不會影響最終結果,這個時候就會進行跳過。
但是對於|(按位與),與按位或相同,是一個位運算符,不會出現跳過的情況。
3. 取反(~)
- 運算規則
取反運算的規則相對簡單,同樣是在二進制位上的運算,那麼遇到0變爲1,遇到1變爲0。
- 實用公式
對於一個數n的取反可以按照公式快讀得出結果:~n = -n - 1
4. 異或(^)
- 運算規則:
其實異或的運算規則很好記,對對碰原則,兩個數相同爲0,不同爲1,如下表:
- 運算律:
- a ^ a = 0
- 交換律:a ^ b = b ^ a
- 結合律:a ^ (b ^ c) = (a ^ b) ^ c
- a ^ b ^ a = b
- 兩數交換的用法:
如果我們需要將兩個數交換,一般都需要引入第三個變量,作爲中間變量或進行運算實現。其實,我們還可以利用異或運算的規律來實現:不借助第三個變量來實現兩個數的交換。
a = a ^ b;
b = a ^ b; -> 使用交換律:b = (a ^ b) ^ b = (b ^ a) ^ b = a
a = a ^ b; -> 此時b的值爲a,a的值爲:(a ^ b) -> a = (a ^ b) ^ a = b
特別注意:雖然異或可以用於兩個數的交換,但是由於異或有一個特別的性質,即a ^ a = 0,所以當兩個數相等時,會導致兩個數都變爲0
三、移位運算
特別注意:對於位移運算不能直接和除法的結果等同,在負數時將不相等
1. 左移(<<)
將一個整數的二進制位向左平移指定的位數,由於是左移就相當於是擴大了數值,並且每移動一位,相當於擴大了二倍。
- 移出的高位將被丟棄
- 右側補零
由於正數的高位都是0,負數的高位都是1,所以在一定的範圍內,經過向左移位不會改變數字的正負。但如果超出了一定的範圍,將會改變數字的正負,並且會充滿隨機性,正負性將取決於最高位(符號位)的數值。
-7 << 2 = -28
-7 << 30 = 1073741824
2. 右移(>>)
將一個整數的二進制位向右平移指定的位數,由於是右移就相當於是縮小了數值,並且每移動一位,相當於縮小了二倍。
- 移出的低位將被丟棄
- 若爲正數,高位補0
- 若爲負數,高位補1
由於符號位在高位的部分,並且在移動的過程中的補位也是根據正負的規則在補,所以右移不會改變正負。
3. 無符號右移(>>>)
無符號右移的計算規則與右移相同,區別在於,不會進行正負的區分,高位一律用0補位。如果原數是一個負數,則可能直接得到一個非常大的正數。
- -27 >> 2 = -7
- -27 >>> 2 = 1073741817
四、運算規則
1. 運算符兩邊精度不一致
- 需要先進行精度的統一,即以高精度爲準,低精度的數據進行補位
- 正數高位補0
- 負數高位補1
2. 移位運算規則
-
移動的位數不應該超過該數字對應的二進制位數
- 得到的結果無數學意義
- 會得到一些極端值結果
-
對於負數在內存中的存儲不清楚的可以參考這篇文章:爲什麼一個byte的存儲範圍是-128~127?