1)bit指「位」,是數據傳輸速度的計量單位,常簡寫爲「b」;Byte指「字節」,是文件大小的計量單位,常簡寫爲「B」。java
2)Byte和bit的換算關係是,1 Byte=8 bits。在電腦上,一個英文字母須要佔用1 Byte的硬盤空間,一個漢字則需佔用2 Byte。 以下圖: android
例如,在咱們java語言中,一個int 佔4 byte,也就是佔32bit,後面我會講到int在一些源碼裏面的妙用程序員
一個數在計算機中的二進制表示形式, 叫作這個數的機器數。機器數是帶符號的,在計算機用一個數的最高位存放符號, 正數爲0, 負數爲1.markdown
將一個數字轉換成二進制(機器數)就是這個數值框架
反碼的表示方法是:正數的反碼是其自己;負數的反碼是在其原碼的基礎上, 符號位不變,其他各個位取反。ide
補碼的表示方法是:正數的補碼就是其自己;負數的補碼是在其原碼的基礎上, 符號位不變, 其他各位取反, 最後+1。 (即在反碼的基礎上+1)函數
十進制原數 | 原碼 | 反碼 | 補碼 |
---|---|---|---|
10 | 0000 1010 | 0000 1010 | 0000 1010 |
-10 | 1000 1010 | 1111 0101 | 1111 0110 |
5 | 0000 0101 | 0000 0101 | 0000 0101 |
-5 | 1000 0101 | 1111 1010 | 1111 1011 |
簡化了計算機的設計,計算機只能進行加法運算,經過補碼的設計,使之能夠在這種設計下,進行減法運算。 好比 1-1 在計算機中執行的 其實是 1 +(-1) 即補碼運算,也就是說,全部計算都是使用該數的補碼,計算完成之後再換回源碼。後面基於負數的計算能夠詳細瞭解。測試
兩個數,從最低位到最高位,一一對應。若是某 bit 的兩個數值對應的值都是 1,則結果值相應的 bit 就是 1,不然爲 0.this
int x = 1; // 0000 0001
int y = 2; // 0000 0010
複製代碼
爲方便後續運算和對比,我後面的運算符均使用這兩個數加密
x&y = 0001 & 0010 = 0000 = 0
y&x = 0010 & 0001 = 0000 = 0
x&x = 0001 & 0001 = 0001 = 1
y&y = 0010 & 0010 = 0010 = 1
複製代碼
兩個數,從最低位到最高位,一一對應。若是某 bit 的兩個數值其中一個是 1,則結果值相應的 bit 就是 1,不然爲 0.
x|y = 0001 | 0010 = 0011 = 3
y|x = 0010 | 0001 = 0011 = 3
x|x = 0001 | 0001 = 0001 = 1
y|y = 0010 | 0010 = 0010 = 2
複製代碼
兩個操做數進行異或時,對於同一位上,若是數值相同則爲 0,數值不一樣則爲 1。
x^y = 0001 ^ 0010 = 0011 = 3
y^x = 0010 ^ 0001 = 0011 = 3
x^x = 0001 ^ 0001 = 0000 = 0
y^y = 0010 ^ 0010 = 0000 = 0
複製代碼
對於這個數每一位 1變0、0變1
~x = ~0000 0001 = 1111 1111 ......(省略) 1111 1110
複製代碼
規則 a >> b 將數值 a 的二進制數值從 0 位算起到第 b - 1 位,總體向右方向移動 b 位,符號位不變,高位空出來的位補數值 0。
y>>1 = 0000...0010(源碼) >>1 = 0000...0010(補碼)>>1 = 0000...0001(運算後的補碼)=0000 ... 0001(源碼)= 1
-y>>1 = 1000 ... 0010(源碼) >>1 = 1111 ... 1110(補碼)>>1 = 1111 ...1111(運算後的補碼)= 1000...0001(源碼)= -1
//其實全部運算都經歷了源碼-補碼-計算-源碼的過程,下面就省略這個過程直接給結論
複製代碼
規則 a << b 將數值 a 的二進制數值從 0 位算起到第 b - 1 位,總體向左方向移動 b 位,符號位不變,低位空出來的位補數值 0。
y<<1 = 0000 ... 0010<<1= 0000 ... 0100 = 4
-y<<1 = 1000 ... 0010<<1= 1000 ... 0100 = -4
複製代碼
公式總結:
無符號右移規則和右移運算是同樣的,只是填充時無論左邊的數字是正是負都用0來填充,無符號右移運算只針對負數計算,而且結果必定是一個正數,由於對於正數來講這種運算沒有意義
y>>>1 = 0000 ... 0010 >>>1 = 0000 ... 0001 = 1
-y>>>1 = 1000 ... 0010 (源碼)>>1 = 1111 ... 1110 (補碼)>> 1 == 0111 ... 1111(計算以後的補碼) = 0111 ... 1111(源碼)= 2147483647
//由於負數最高位補0 變成了正數,正數的補碼源碼都是它本身,因此變成了一個很大的數,這一點要特別注意。
複製代碼
x = x^y = 0001 ^ 0010 = 0011 = 3
y = y^x = 0010 ^ 0011 = 0001 = 1
x = x^y = 0011 ^ 0001 = 0010 = 2
複製代碼
正好互換了,因此之後就能夠這麼寫:
x^=y,
y^=x,
x^=y
複製代碼
基於以上特性,能夠實現對一個數字進行加密,一串數字,對一箇中間數字進行異或運算,獲得加密數據,解密再次對中間數字進行異或便可。
平時你們寫代碼是否遇到過這樣的場景:一個類,有一個屬性是用boolean表示,隔了一段時間,又須要新加一個boolean表示新的屬性。。。因此就如同下面的代碼:
public class Human {
/** * 是不是學生 */
private boolean isStudent;
/** * 是否已經成年 */
private boolean isAdult;
/** * 是否單身 */
private boolean isSingle;
// private boolean is.....
public void setStudent(boolean student) {
isStudent = student;
}
public boolean isStudent() {
return isStudent;
}
// setter and getter...
}
複製代碼
那如今,經過位運算,咱們能夠這麼寫:
public class Human {
/** * 是不是學生 */
public static final int IS_STUDENT = 1;
/** * 是否已經成年 */
public static final int IS_ADULT = 2;
/** * 是否單身 */
public static final int IS_SINGLE = 4;
private int properties;
public void setProperties(int properties) {
this.properties = properties;
}
public boolean isStudent() {
return (properties & IS_STUDENT) != 0;
}
public boolean isAdult() {
return (properties & IS_ADULT) != 0;
}
public boolean isSingle() {
return (properties & IS_SINGLE) != 0;
}
@Override
public String toString() {
return "是不是學生 " + isStudent() + " 是否成年 " + isAdult() + " 是否單身 " + isSingle();
}
}
複製代碼
咱們在傳入參數只提供一個setProperties 方法,咱們在傳入參數的地方用 「|」運算符,分隔咱們想要指定的屬性,下面是測試代碼:
public static void main(String args[]) {
Human human = new Human();
human.setProperties(Human.IS_STUDENT);
System.out.println(human.toString());
human.setProperties(Human.IS_STUDENT | Human.IS_SINGLE);
System.out.println(human.toString());
human.setProperties(Human.IS_SINGLE | Human.IS_ADULT);
System.out.println(human.toString());
human.setProperties(Human.IS_STUDENT | Human.IS_SINGLE | Human.IS_ADULT);
System.out.println(human.toString());
}
複製代碼
輸出結果
是不是學生 true 是否成年 false 是否單身 false
是不是學生 true 是否成年 false 是否單身 true
是不是學生 false 是否成年 true 是否單身 true
是不是學生 true 是否成年 true 是否單身 true
複製代碼
原理分析: 首先,注意看,我定義的常量除了0以外都是一、二、4 、即2 ^ n
a = 1 = 0000 0001
b = 2 = 0000 0010
c = 4 = 0000 0100
d = 8 = 0000 1000
e = 16 = 0001 0000
f = 32 = 0010 0000
//......即我能夠定義最多31個數(第32位表正負)
複製代碼
這樣一來,咱們用「|」 運算符將其中任意兩個或者多個進行計算的時候,其實是把它們按照本身的佔位保存了例如:
int x = c|d|e = 0001 1100 = 28
//此時判斷x是否包含 c 或者 d
//用&便可
int y = z&c = 0000 0100 = 4 = c
int z = z&d = 0000 1000 = 8 = d
其實只要結果不爲 0000 0000 也就是0 表示&運算符成立 ,就能夠判斷是否包含該數字,即上面函數的方法的定義。
複製代碼
下面我看看,衆所周知,咱們Android LinearLayout 有這樣一個屬性"showDividers":
<LinearLayout
android:showDividers="beginning|middle|end"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
複製代碼
分別表示你能夠在view的幾個位置展現分割線,爲何能夠用這個屬性表示三個位置呢?咱們進入源碼看看:
private int mShowDividers;
public static final int SHOW_DIVIDER_NONE = 0;
public static final int SHOW_DIVIDER_BEGINNING =1;
public static final int SHOW_DIVIDER_MIDDLE = 2;
public static final int SHOW_DIVIDER_END = 4;
public void setShowDividers( int showDividers) {
if (showDividers == mShowDividers) {
return;
}
mShowDividers = showDividers;
setWillNotDraw(!isShowingDividers());
requestLayout();
}
protected boolean hasDividerBeforeChildAt(int childIndex) {
if (childIndex == getVirtualChildCount()) {
return (mShowDividers & SHOW_DIVIDER_END) != 0;
}
boolean allViewsAreGoneBefore = allViewsAreGoneBefore(childIndex);
if (allViewsAreGoneBefore) {
return (mShowDividers & SHOW_DIVIDER_BEGINNING) != 0;
} else {
return (mShowDividers & SHOW_DIVIDER_MIDDLE) != 0;
}
}
複製代碼
我只列出了上面核心的幾行代碼,首先,全部的的顯示屬性都是 一個int 的 mShowDividers 表示,set方法用「|」 進行指定,在看核心的代碼在onDraw 方法中繪製分割線的時候,會調用這個方法,判斷方法就是用&運算符。 另外,View.MeasureSpec 和Gravity 的類也用到了位運算符,具體這裏就不深刻探討了。
參考源碼: Android :