回顧
首先來回顧一下位運算,什麼是位運算呢?
位運算就是直接對整數在記憶體中的二進位制位進行操作。
在 Java 語言中,位運算有如下這些:
-
左移(<<)。
-
右移(>>)。
-
無符號右移(>>>)。
-
與(&)。
-
或(|)。
-
非(~)。
-
異或(^)。
在本篇文章中,我們所需要用到的有如下幾個(其他的後續文章再講):
-
&(與運算):只有當兩方都為 true 時,結果才是 true,否則為 false。
-
|(或運算):只要當一方為 true 時,結果就是 true,否則為 false。
-
^(異或運算):只要兩方不同,結果就是 true,否則為 false。
以 true、false 為例:
true & true = true
true & false = false
true | false = true;
false | false = false;
true ^ true = false;
true ^ false = true;
複製程式碼
以數字運算為例:
6 & 4 = ?
6 | 4 = ?
6 ^ 4 = ?
複製程式碼
當以數字運算時,我們首先需要知道這些數字的二進位制,假設 6 是 int 型別,那麼其二進位制如下:
00000000 00000000 00000000 00000110
複製程式碼
在 Java 中,int 佔了 4 個位元組(Byte),一個位元組呢又等於 8 個 Bit 位。所以 int 型別的二進位制表現形式如上。
在這裡為方便講解,直接取後 8 位:00000110。
4 的二進位制碼如下:
00000100
複製程式碼
在二進位制碼中,1 為 true,0 為 false,根據這個,我們再來看看 6 & 4
的運算過程:
00000110
00000100
-----------
00000100
複製程式碼
對每位的數進行運算後,結果為 4。
再來看看 | 運算:
6 | 4 = ?
複製程式碼
6 和 4 的二進位制上面已經說了:
00000110
00000100
-----------
00000110
複製程式碼
可以發現最後的結果是 6。
最後再來看看 ^ 運算:
6 ^ 4 = ?
複製程式碼
00000110
00000100
-----------
00000010
複製程式碼
結果是 2。
應用
通過上面的例子,我們已經回顧了 & 、 | 以及 ^ 運算。現在來將它應用到實際的應用中。
假如我們現在要定義一個人的模型,這個人可能會包含有多種性格,比如說什麼樂觀型、內向型啦...
要是想要知道他包含了哪種性格,那麼我們該如何判斷呢?
可能在第一時間會想到:
if(這個人是樂觀性){
....
}else if(這個人是內向型){
...
}
複製程式碼
那麼如果有很多種性格呢?一堆判斷寫起來真的是很要命..
下面就來介紹一種更簡單的方式。首先來定義一組數:
public static final int STATUS_NORMAL = 0;
public static final int STATUS_OPTIMISTIC = 1;
public static final int STATUS_OPEN = 2;
public static final int STATUS_CLOSE = 4;
複製程式碼
把它們轉換為二進位制:
0000 0000 0000 0000
0000 0000 0000 0001
0000 0000 0000 0010
0000 0000 0000 0100
複製程式碼
發現其中二進位制的規律沒有?都是 2 的次冪,並且二進位制都只有一個為 1 位,其他都是 0 !
然後再來定義一個變數,用於儲存狀態(預設值是 0):
private static int mStatus = STATUS_NORMAL;
複製程式碼
當我們要儲存狀態時,直接用 | 運算即可:
mStatus |= STATUS_OPTIMISTIC;
複製程式碼
儲存的運算過程如下:
00000000
執行 | 運算(只要有 1 則為 1)
00000001
-----------
00000001 = 1
複製程式碼
相當於就把這個 1 儲存到 0 的二進位制當中了。
那麼如果要判斷 mStatus
中是否有某個狀態呢?使用 & 運算:
System.out.println((mStatus & STATUS_OPTIMISTIC) != 0);// true,代表有它
複製程式碼
計算過程如下:
00000001
執行 & 運算(都為 1 才為 1)
00000001
-----------
00000001 = 1
複製程式碼
再來判斷一個不存在的狀態 mStatus & STATUS_OPEN
:
System.out.println((mStatus & STATUS_OPEN) != 0);// false,代表沒有它
複製程式碼
計算過程如下:
00000001
00000010
-----------
00000000 = 0
複製程式碼
可以發現,因為 STATUS_OPEN
這個狀態的二進位制位,1 的位置處,mStatus
的二進位制並沒有對於的 1,而又因為其他位都是 0,導致全部歸 0,計算出來的結果自然也就是 0 了。
這也就是為什麼定義狀態的數字中,是 1、2、4 這幾位數了,因為他們的特定就是二進位制只有一個為 1 的位,其他位都是 0,並同其他數位 1 的位不衝突。
如果換成其他的數,就會有問題了。比如說 3:
mStatus |= 3
複製程式碼
計算過程:
00000000
00000011
-----------
00000011 = 3
複製程式碼
運算完畢,這時候 mStatus
中已經儲存了 3 這個值了,我們再來判斷下是否存在 2:
System.out.println((mStatus & 2) != 0);// true,代表有它,但是其實是沒有的
複製程式碼
00000011
00000010
-----------
00000010 = 2
複製程式碼
結果是 true,但是其實我們只儲存了 3 到 mStatus
中,結果肯定是錯誤的。
所以我們在定義的時候,一定不要手滑定義錯了數字。
儲存和判斷已經說了,那麼如何取出呢?這時候就要用到 ^ 運算了。
假如現在 mStatus
中已經儲存了 STATUS_OPTIMISTIC
狀態了,要把它給取出來,這樣寫即可:
mStatus ^= STATUS_OPTIMISTIC
複製程式碼
其中的運算過程:
00000001
執行 ^ 運算,兩邊不相同,則為 true
00000001
-----------
00000000
複製程式碼
可以看到狀態又回到了最初沒有儲存 STATUS_OPTIMISTIC
狀態的時候了。
最後再來看一個取出的例子,這次是先儲存兩個狀態,然後再取出其中一個:
mStatus |= STATUS_OPTIMISTIC
mStatus |= STATUS_OPEN
複製程式碼
儲存完後,mStatus
的二進位制為:
00000011
複製程式碼
再來取出 STATUS_OPEN
這個狀態:
mStatus ^= STATUS_OPEN
複製程式碼
運算過程:
00000011
00000010
-----------
00000001
複製程式碼
mStatus
現在就只有 STATUS_OPTIMISTIC
的狀態了。
總結
通過 |、^、& 運算,我們可以很方便快捷的對狀態值進行操作。當然,位運算的應用不僅限於狀態值,知道了其中的二進位制運算原理後,還有更多的其他應用場景,等著你去發現。