基礎大講堂-java 位運算符

全部數值都是 2 進制

軟件開發者都知道 10 進制、16 進制、8 進制。 好比數字 10 的各位進制形式表現以下。html

十進制:10
八進制:012
十六進制:0x0a
二進制:1010
複製代碼

咱們能夠打開系統的自帶的計算器 (Win 鍵 + R –> 輸入 CMD 打開命令行窗口 –> calc 按回車),看看上面的結論。java

img

雖然有不少種進制,可是實際上計算機所認識的數據只有 0 和 1, 所以全部的數值無論它是十進制、十六進制也好都會通通在底層被翻譯成二進制數值。算法

int a = 5;
//0101 就是 a 的二進制表示。 

int b = 520;
//1000001000 就是 b 的二進制表示
複製代碼

bit、byte、world

  1. bit (位) bit 電腦記憶體中最小的單位,在二進位電腦系統中,每一 bit 能夠表明 0 或 1 的數位訊號。因此它能表示的數字範圍就是 0 ~ 1。
  2. byte (字節) 一個 byte 由 8 bit 組成,因此理論上一個 byte 能表示的數據範圍是 0 ~ 255。
  3. word (字) 一個 word 由 2 byte 組成,因此理論上一個 word 能表示的數據範圍是 0 ~ 65535。

你們能夠看這張圖加深下理解。網絡

img

32 位與 64 位操做系統。

通常計算機設備上,CPU 主要有 32 位和 64 位(固然,單片機有 8 位和 16 位),32 位 CPU 可以尋址的範圍是 4 GB。因此過去的電腦設備內存最高通常只能到達 4 GB。後來,隨着芯片技術的發展,愈來愈多的機器採用了 64 位 CPU。這使得機器的最大內存能夠爲 16 GB。測試

那麼好,咱們再來談談 32 位操做系統與 64 位操做系統。實際上它們分別是針對 CPU 類型設計的軟件系統。優化

32 bit 是 4 byte。一般一條 CPU 指令是 4 byte。在 32 位操做系統上,若是一條 CPU 指令是 4 byte,那麼 CPU 執行一次可以讀取 32 bit 內容,因此一個指令週期內就可以完成指令,若是一條 CPU 指令是 8 byte 的話,那麼 32 位操做系統就須要經過 2 個指令週期才能完成指令的讀取,而對應的 64 位操做系統由於一次可以讀取 64 bit 內容,因此它在一個指令週期就可以讀取指令。因此,理論上,64 位的操做系統是要比 32 位操做系統要快 1 倍。加密

但還有幾個須要你們注意的地方是:spa

  1. 64 位 CPU 機器能夠安裝 32 位操做系統,但效率天然跟 32 位操做系統同樣。
  2. 32 位 CPU 機器也能夠安裝 64 位操做系統。
  3. 64 位 CPU 機器安裝 64 位操做系統才最有效率,但跟軟件優化也有關係。

不一樣的操做系統平臺,給 C/C++ 基本數據類型變量分配的字節是不同的。操作系統

32 位編譯器:命令行

char1個字節
  char*(即指針變量): 4個字節(32位的尋址空間是2^32, 即32個bit,也就是4個字節。同理64位編譯器)
  short int : 2個字節
  int4個字節
  unsigned int : 4個字節
  float:  4個字節
  double:   8個字節
  long:   4個字節
  long long:  8個字節
  unsigned long:  4個字節
複製代碼

64 位編譯器:

char1個字節
  char*(即指針變量): 8個字節
  short int : 2個字節
  int4個字節
  unsigned int : 4個字節
  float:  4個字節
  double:   8個字節
  long:   8個字節
  long long:  8個字節
  unsigned long:  8個字節
複製代碼

上面講的是 C/C++ 在不一樣平臺上的字節長度差異,可是對於 Java 而言,因爲 Java 是跨平臺語言,因此 JVM 表現下的基礎數據字節長度其實都是一致的。

int4 個字節。

short2 個字節。

long8 個字節。

byte1 個字節。

float4 個字節。

double8 個字節。

char2 個字節。

booleanboolean屬於布爾類型,在存儲的時候不使用字節,僅僅使用 1 位來存儲,範圍僅僅爲01,其字面量爲truefalse複製代碼

咱們能夠看到 Java 與 C/C++ 的基本數據類型字節長度有些不一致,因此涉及到網絡通訊交互或者是 JNI 開發時,數據的轉換有時須要考慮下基礎的字節長度。

本篇文章的主要內容是 Java 中的位操做,因此基礎數據長度也是以 Java 中定義的爲準。

原碼 反碼 補碼

咱們已經知道了一個 int 型數值是 4 個字節。每一個字節有 8 位。但對於一個 int 或者其它整數類型如 (long)的數值而言還要注意的是,它的最高位是符號位。

  • 最高位爲 0 表示正數。
  • 最高位爲 1 表示負數

原碼 將一個數字轉換成二進制就是這個數值的原碼。

int a = 5; //原碼 0000 0000 0000 0101
int b = -3;  //原碼 1000 0000 0000 0011
複製代碼

反碼 分兩種狀況:正數和負數

  • 正數 正數的反碼就是原碼。
  • 負數 負數的反碼是在原碼的基礎上,符號位不變 其它位都取反。
5 的原碼:0000 0000 0000 0101

-3 的原碼:1000 0000 0000 0011
-3 的反碼:1111 1111 1111 1100
複製代碼

補碼 仍然分正數和負數兩種狀況

  • 正數 正數的補碼就是原碼。
  • 負數 負數的補碼在反碼的基礎上加 1。
5 的補碼:0000 0000 0000 0101


-3 的反碼:1111 1111 1111 1100
-3 的補碼: 1111 1111 1111 1101
複製代碼

計算機在進行數值運算的時候,是經過補碼錶示每一個數值的。

好比

5 - 3 = 5 + ( -3 )
至關於 0000 0000 0000 0101 + 1111 1111 1111 1101
    = 1 0000 0000 0000 0010
複製代碼

最後的結果是 1 0000 0000 0000 0010 這樣的二進制,因爲 int 類型只有 4 byte,因此最高位產生了溢出,進位 1 被丟棄。結果就變成了 0010 也就是 2,5 - 3 = 2 沒有毛病。

img

位運算符 &、|、~、^、>>、<<

位運算符包含與運算符、或運算符、取反運算符、異或運算符、左移運算符和右移運算符。在下面的內容中,我將會一一講解。

須要注意的是,下面測試用的數據都是 int 類型,int 類型是 4 個字節長度,可是爲了方便說明示例中用的數值我都用 1 個字節表示。但願不會給你們形成困擾。

& 與運算符

規則 與運算時,進行運算的兩個數,從最低位到最高位,一一對應。若是某 bit 的兩個數值對應的值都是 1,則結果值相應的 bit 就是 1,不然爲 0.

0 & 0 = 0,

0 & 1 = 0,

1 & 1 = 1
複製代碼

3 & 5 = 1 這是由於

0000 0011

&

0000 0101

=

0000 0001
複製代碼

按照規則,將兩個數值按照低位到高位一一對齊運算,由於只有第 0 位都爲 1,因此計算結果爲 1.

| 或運算符

規則 與運算時,進行運算的兩個數,從最低位到最高位,一一對應。若是某 bit 的兩個數值對應的值只要 1 個爲 1,則結果值相應的 bit 就是 1,不然爲 0。

0 | 0 = 0,

0 | 1 = 1,

1 | 1 = 1
複製代碼

3 | 5 = 7 這是由於

0000 0011

|

0000 0101

=

0000 0111
複製代碼

~ 取反運算符

規則 對操做數的每一位進行操做,1 變成 0,0 變成 1。

~5 =>  0000 0101   ~  => 1111 1010
複製代碼

^ 異或運算符

規則 兩個操做數進行異或時,對於同一位上,若是數值相同則爲 0,數值不一樣則爲 1。

1 ^ 0 = 1,

1 ^ 1 = 0,

0 ^ 0 = 0;
複製代碼

3 ^ 5 = 6, 這是由於

0000 0011

|

0000 0101

=

0000 0110
複製代碼

值得注意的是 3 ^ 5 = 6, 而 6 ^ 5 = 3

0000 0110

|

0000 0101

=

0000 0011
複製代碼

針對這個特性,咱們能夠將異或運算做爲一個簡單的數據加密的形式。好比,將一個 mp4 文件全部數值與一個種子數值進行異或獲得加密後的數據,解密的時候再將數據與種子數值進行異或一次就能夠了。

因此說異或運算能夠做爲簡單的加解密運算算法。

>> 右移運算符

規則 a >> b 將數值 a 的二進制數值從 0 位算起到第 b - 1 位,總體向右方向移動 b 位,符號位不變,高位空出來的位補數值 0。

5 >> 1 ===>  0000 0000 0000 0101 >> 1  = 0000 0000 0000 0010 = 2
7 >> 2 ===>  0000 0000 0000 0111 >> 2  = 0000 0000 0000 0001 = 1
9 >> 3 ===>  0000 0000 0000 1001 >> 3  = 0000 0000 0000 0001 = 1
11 >> 2 ===> 0000 0000 0000 1011 >> 2  = 0000 0000 0000 0010 = 2
複製代碼

你們發現什麼規律沒有?a >> b = a / (2 ^ b) , 因此 5 >> 1= 5 / 2 = 2,11 >> 2 = 11 / 4 = 2。

<< 左移運算符

規則 a << b 將數值 a 的二進制數值從 0 位算起到第 b - 1 位,總體向左方向移動 b 位,符號位不變,低位空出來的位補數值 0。

5 << 1 ===>  0000 0000 0000 0101 << 1  = 0000 0000 0000 1010 = 10
7 << 2 ===>  0000 0000 0000 0111 << 2  = 0000 0000 0001 1100 = 28
9 << 3 ===>  0000 0000 0000 1001 << 3  = 0000 0000 0100 1000 = 72
11 << 2 ===> 0000 0000 0000 1011 << 2 = 0000 0000 0010 1100 = 44
複製代碼

很明顯就能夠看出 a <<b = a * (2 ^ b)

綜合上面兩個能夠看到,若是某個數值右移 n 位,就至關於拿這個數值去除以 2 的 n 次冪。若是某個數值左移 n 位,就至關於這個數值乘以 2 ^ n。

>>> 無符號右移運算符

規則 a >>>b 忽略符號位,空位都以0補齊

5 >>> 1 ===>  0000 0000 0000 0101 >>> 1  = 000 0000 0000 1010 = 10
7 >>>2 ===>  0000 0000 0000 0111 >>> 2  = 1000 0000 0001 1100 = 28
-5 >>> 1 ===>  1111 1111 1111 1111 1111 1111 1111 1011 >>> 1  = 0111 1111 1111 1111 1111 1111 1111 1101 = 10
-7 >>>2 ===>  1000 0000 0000 0111 >>> 2  = 1000 0000 0001 1100 = 28
複製代碼

總結

Java 的位運算內容就是上面講到的這些,這些東西都很是簡單。可是有時候簡單的東西卻能很大程序上提升開發效率。之因此想起寫這篇文章,是由於在閱讀 Android 相關代碼時,正好看到了位運算的身影,只以爲很是好用和巧妙。下一篇文章我會專門來介紹 Java 位運算在 Android 源碼中的巧妙應用。文章寫完後我會放上連接地址。

參考內容

32 位機和 64 位機的區別及基本數據類型佔字節數

相關文章
相關標籤/搜索