Java千問:Java語言位運算符詳解

不少編程語言都有位運算符,Java語言也不例外。在Java語言中,提供了7種位運算符,分別是按位與(&)、按位或(|)、按位異或(^)、取反(~)、左移(<<)、帶符號右移(>>)和無符號右移(>>>)。這些運算符當中,僅有~是單目運算符,其餘運算符均爲雙目運算符。在講解這些運算符的使用以前,必須瞭解一個常識,那就是:位運算符是對long、int、short、byte和char這5種類型的數據進行運算的,咱們不能對double、float和boolean進行位運算操做。下面就來詳細講解這7種位運算符的使用方法。面試

1、按位與運算符

按位與運算符的寫法是一個」&」符號,與」不短路的邏輯與運算符」寫法是徹底同樣的,但意義不一樣。邏輯與運算是對布爾型數據進行運算,而按位與運算是對二進制位上的數值進行計算。按位與運算符的運算規則以下圖所示:
Java千問:Java語言位運算符詳解
運算規則總結成一句話就是:若是兩個二進制位上的數都是1,那麼運算結果爲1,其餘狀況運算結果均爲0。下面舉例說明按位與運算符的運算過程,咱們用數字5和6進行按位與運算。這個過程能夠用下圖表示:
Java千問:Java語言位運算符詳解
運算過程當中,首先把5和6這兩個數字轉換爲補碼,以後還要把這兩個數字按位對齊,而後一一把兩個相應的二進制位上的數字進行按位與運算,運算獲得的二進制串就是最終的結果。按照補碼反向轉換爲十進制數字的規則,能夠計算出5&6的運算結果是4。在這裏要提醒你們一句:進行位運算的時候,最左邊的符號位也是要參與運算的。編程

2、按位或運算符

按位或運算符的寫法是一個」|」符號,與」不短路的邏輯或運算符」寫法相同,它的運算規則也很簡單,以下圖所示:編程語言

Java千問:Java語言位運算符詳解
運算規則歸納成比較好記的一句話就是:兩個二進制位上的數字若是都爲0,那麼運算結果爲0,不然運算結果是1。同按位與運算同樣,符號位也要參與運算。下面咱們仍是用5和6爲例來說解一下按位或的運算過程,以下圖所示:ide

Java千問:Java語言位運算符詳解
首先仍是把這兩個數字轉換成補碼形式,以後把相應的二進制位上的數字進行按位或運算:若是兩個二進制數都是0,計算結果爲0,其餘狀況計算結果均爲1。按照這個規則把每一位上的數字都計算一遍後,獲得二進制的運算結果是111,這個運算結果轉換爲十進制數是7。學習

3、按位異或運算符

按位異或運算符寫法是」^」,它的運算規則以下圖:加密

Java千問:Java語言位運算符詳解
如上圖,運算規則爲:兩個二進制位上的數字若是相同,則運算結果爲0,若是兩個二進制位上的數字不相同,則運算結果爲1。下面咱們仍是用5和6爲例來說解一下異或的運算過程,以下圖:
Java千問:Java語言位運算符詳解
首先仍是把這兩個數字轉換成補碼形式,以後把相應的二進制位上的數字進行異或運算,若是對應的兩個二進制位上的數相同,計算結果爲0,不然計算結果爲1。按照這個規則把每一位上的數字都計算一遍後,獲得二進制的運算結果是11,這個運算結果轉換爲十進制數是3。
關於異或運算符,有不少很是有用的特性,咱們在這裏梳理總結一下。
Ⅰ、異或運算符知足交換律
也就是說,a^b與b^a是等價的,雖然a和b交換了位置,但仍是會運算出相同的結果。這個規律還能夠推廣到N個操做數,也就是說,若是有N個變量都參與了異或運算,那麼它們的位置不管如何交換,運算的結果都是相同的。設計

Ⅱ、任何兩個相同的數字進行異或操做,所獲得的結果都必然爲0
這個特性並不難理解,由於兩個相同的數字,換算成補碼後,每一個二進制位上的數也都相同,這樣在進行異或運算時,按照運算規則,每一個二進制位上獲得的運算結果也都是0,這N個0所組成的二進制串就是數字0的補碼。咱們能夠利用這個特性快速的判斷兩個整數是否相同。另外,利用這個特性還能夠實現內存的快速清零操做,好比咱們能夠在代碼中寫上a=a^a;這條語句能快速的把變量a所佔據的那幾個字節的內存迅速清零。3d

Ⅲ、對於任意一個二進制位來講,這個位上的數與0進行異或運算,運算結果與這個二進制位上的數是相同的,而與1進行異或運算,結果與這個二進制位上的數字相反
注意,咱們如今說的是二進制位上的數字,所謂相反不是說原來這個位上是1,運算結果是-1,而是說原來是1,運算結果爲0,原來若是是0,運算結果是1,這纔是此處所說的」相反」的概念。這個特性也很是好理解,小夥伴們必定要記住它,在之後進行一些位運算操做的時候常常會用到這個特性。

Ⅳ、對於任何兩個整數a和b,a^b^b等於a

這個結論爲何成立呢?簡單說來,就是由於這個表達式中有b^b,而b^b的結果爲0,前文已講過,任何一個數與0進行按位異或操做,結果仍然是這個數自己,因此,a^b^b等於a。這個特性在加密運算方面有着很廣泛的應用。咱們能夠把a看成要加密的數據,而把b看成密鑰。a異或b就是把a用密鑰b進行了加密操做,當須要解密時,仍然以b做爲密鑰,再進行一次異或就實現瞭解密。
這個特性還能夠推出另一個結論:對於任何兩個整數a和b,a^b^a等於b。咱們可以獲得這個結論的緣由也很簡單,就是由於按照交換律,a^b與b^a的運算結果是同樣的,因此a^b^a等價於b^a^a,這個表達式中出現了a^a,a^a的值也爲0,因此整個表達式的其實就至關於b^0,最終結果仍是b。
但願你們可以牢記以上這些結論,在後續的文章中,會講解如何用這些結論去解決實際問題。視頻

4、按位取反運算符

按位取反運算符寫法是」~」,它的運算規則是:對每一個二進制位進行取反操做,所謂取反就是原來二進制位上若是是0,那麼就變成1,反之,若是原來二進制位上是1,那麼就變爲0。取反運算符是一個單目運算符,因此只須要一個操做數就能夠了。咱們以數字5爲例講解按位取反的運算過程:
Java千問:Java語言位運算符詳解
首先把數字5轉換成補碼形式,以後把每一個二進制位上的數字進行取反,若是是0就變成1,若是1就變成0,通過取反後獲得的二進制串就是運算結果,這個運算結果被還原爲十進制數是-6。取反運算符的運算規則也很是容易理解,可是在這裏老師須要提醒各位讀者注意:若是是對變量進行取反操做,那麼通過操做以後,變量的值並不會發生變化!爲方便小夥伴們理解,請看下圖:
Java千問:Java語言位運算符詳解
從程序運行的結果能夠看出:輸出a的值仍是5,這說明變量a通過取反獲得的那個-6並無被賦值到變量a中,經過這個例子能夠證實,取反運算並無對變量從新賦值的功能,取反運算的結果只是臨時保存在操做數棧中,變量自己的值不會因取反操做而發生改變。blog

下面再來說解一下與位移相關的運算符。所謂」位移」就是指在內存中對二進制串進行移動的操做。只要是移動操做,就必然會涉及到如下幾個問題,怎樣表示移動方向?怎樣表示移動的位數?移動以後空出來的二進制位用什麼來填充?移動以後跑到原來內存單元外面的那些數字怎麼處理?符號位要不要跟着一塊兒移動?這些問題都是咱們學習位移操做要弄清楚的細節。各位小夥伴能夠帶着這些問題來學習位移相關的運算符。與位移相關的運算符有三個,分別是左移(<<)、帶符號右移(>>)、無符號右移(>>>)。

5、左移運算符

左移運算符的寫法是」<<「,看上去向兩個向左的箭頭,表示要把二進制數據在內存空間中向左邊移動。使用左移運算符時,把想進行位移操做的操做數放最左面,以後寫上左移運算符,在左移運算符的右邊寫上移動的位數。例如:"5<<2"就表示對數字5進行左移2位的操做。下圖展現了進行左移操做以後,二進制串在內存中是怎樣變化的:
Java千問:Java語言位運算符詳解
能夠看到這個二進制串在內存中總體向左移動了兩位,那麼最左邊的兩位就跑到內存單元的外面去了,這兩位數字將會被捨棄,右邊空出的兩位用0補齊。
左移運算有乘以2的N次方的效果。一個數向左移動1位,就至關於乘以2的1次方,移動兩位就至關於乘以2的2次方,也就是乘以4。位移操做在實際運算時遠遠快於乘法操做,因此在某些對運算速度要求很是高的場合,能夠考慮用左移代替乘以2的N次方的乘法操做。可是須要提醒你們注意三個細節:
首先:位移操做同取反操做同樣,並不能改變變量自己的值,所能改變的僅是存儲在操做數棧中那個數據的值,不理解這句話意思的小夥伴看下圖:

Java千問:Java語言位運算符詳解
其次:當位移的位數不少時,致使最左邊的符號位發生變化,就再也不具備乘以2的N次方的效果了。好比十進制的5轉換爲補碼形式是:前面29個0最後3位是101,若是移動29位,那麼最前面的符號位就變成了1,此時運算的結果就成爲了一個負數,再也不是5乘以2的29次方的乘法結果。
最後:對於byte/short/int三種類型的數據,Java語言最多支持31位的位移運算,對於long類型的數據而言,最多支持63位的位移運算。這多是由於Java語言的設計者認爲位移的偏移量已經超過存儲數據自己的長度,沒有什麼意義。小夥伴們能夠試一下數字5左移32位是什麼結果。

6、帶符號右移運算符

右移運算分爲兩種,分別是帶符號右移和無符號右移。首先咱們來講說帶符號右移運算符。帶符號右移運算符的寫法是」>>「,與左移運算符的方向剛好相反。所謂帶符號右移就是指當二進制串向右邊移動之後,左邊空出的位用」符號位上的數字」填充,說的更直白一點,若是是正數,二進制串右移的時候用0來填充左邊的空位,而對於負數而言,右移的時候用1來填充左邊的空位,以下圖:

Java千問:Java語言位運算符詳解
從圖上能夠清楚的看到帶符號右移操做在二進制串移動以後左邊空位是怎樣被填充的。以前強調過,左移操做具備乘以2的N次方的效果,其實帶符號右移也具備」相似」除以2的N次方的效果。請注意,這裏說的是」相似」除以2的N次方的效果,爲何要加上」相似」兩個字呢?就是由於對於正數而言,帶符號右移以後產生的數字確實等於除以2的N次方,可是對於負數而言,帶符號右移是在除以2的N次方的結果之上還要減去1。好比對於正5,帶符號右移兩位的結果是1,而對於-5,帶符號右移兩位的結果是-2,也就是-5被2的2次方整除再減去1的結果。
帶符號右移的操做能夠保證移動以前和移動以後數字的正負屬性不變,原來是正數,無論移動多少位,移動以後仍是正數,原來是負數,移動以後仍是負數。另外,咱們還能夠繼續深挖一下這個特性,從而獲得一個結論:對於任何一個byte、short或者int類型的數據而言,帶符號右移31位以後,獲得的必然是0或者是-1。對於long類型的數據而言,帶符號右移63位以後,獲得的也必然是0或者是-1。可以得出這個結論的依據也很簡單,就是由於對於byte、short和int類型的變量而言,若是是正數,帶符號右移31位以後產生的二進制串必然所有是0,轉換成對應的十進制數就是0;而對於負數而言,帶符號右移31位以後產生的二進制串必然所有是1,轉換成十進制數就是-1。對於long類型的數據,帶符號右移63位也具備相同效果。

7、無符號右移運算符

前文已說過:右移運算分爲兩種,分別是帶符號右移和無符號右移。如今再來說解無符號右移。無符號右移運算符的寫法是」>>>」,比帶符號右移多了一個」>」。帶符號右移的運算規則與無符號右移的運算規則差異就在於:無符號右移在二進制串移動以後,空位由0來補充,與符號位是0仍是1毫無關係,以下圖:
Java千問:Java語言位運算符詳解
以上圖片展現了無符號右移的運算規則。對於正數而言,無符號右移和帶符號右移沒有什麼區別,而對於負數而言,通過無符號右移會產生一個正數,由於最左邊的符號位被0填充了。

到此爲止,咱們已經講解了所有的7個位運算符。不少小夥伴都會認爲位運算沒有太多的實用價值,學習位運算操做也只是爲了應付面試。其實這種想法是徹底錯誤的。位運算在不少場合都有着深刻應用,可以解決不少實際問題。隨後的幾篇文章將詳細講解位運算在實際開發中的應用技巧。

如想系統學習Java編程,歡迎觀看我在本站的視頻課程。

相關文章
相關標籤/搜索