byte[i] & 0xFF原因(byte爲什麼要&上0xff?)
- 先來看一段代碼:
private String changeHex(byte[] bs){
char[] hexArray = "0123456789abcdef".toCharArray(); // 將字符串轉換爲一個字符數組
char[] hexChars = new char[bs.length * 2]; // 創建一個bs字符數組兩倍的字符數組
for ( int j = 0; j < bs.length; j++ ) {
int v = bs[j] & 0xFF; // 保持二進制補碼的一致性 因爲byte類型字符是8bit的 而int爲32bit 會自動補齊高位1 所以與上0xFF之後可以保持高位一致性 當byte要轉化爲int的時候,高的24位必然會補1,這樣,其二進制補碼其實已經不一致了,&0xff可以將高的24位置爲0,低8位保持原樣,這樣做的目的就是爲了保證二進制數據的一致性。
hexChars[j * 2] = hexArray[v >>> 4];
hexChars[j * 2 + 1] = hexArray[v & 0x0F];
}
}
- 簡單分析這段代碼:
首先這段代碼的作用顯而易見:將java中的byte類型轉換爲String類型~分析其實大致已經在代碼中給出註釋,&這裏先暫且不提,我們先看看>>>這個移位操作符,因爲平時自己用的不多,寫到這裏就純粹當做給自己補充下知識吧。。
java中有三種移位運算符:
<< : 左移運算符,右邊空出的位用0填補,高位左移溢出則捨棄該高位:num << 1,相當於num乘以2
>> : 右移運算符,左邊空出的位用0或者1填,正數用0負數用1填,低位右移溢出則捨棄該位。num >> 1,即num除以2
>>> : 無符號右移,忽略符號位,空位都以0補齊
- 接下來我們看一個小Demo
package com.czc;
import org.junit.Test;
public class ThreadTest {
@Test
public void Test() {
byte[] a = new byte[10];
a[0] = -127;
a[1] = -128;
System.out.println(a[0]);
int c = a[0] & 0xFF;
System.out.println(c);
a[2] = (byte) (a[0]<<1);
System.out.println(a[2]);
a[3] = (byte) (a[0]>>1);
System.out.println(a[3]);
}
}
很簡單的一個小Demo,嗯,接下來,先看這個小Demo,再回頭去看之前的那個byte類型轉String類型的&
- 看看Demo執行結果
-127
129
2
-64
嗯,結果似乎有不少地方與我們所想似乎並不一致,好了,這裏我們還需要有起碼的認識才行.
- 這裏我們還需要知道的是:
在計算機系統中,數值一律用補碼來表示(存儲)。 主要原因:使用補碼,可以將符號位和其它位統一處理;同時,減法也可按加法來處理。
機器數:一個數在計算機中的二進制表示形式, 叫做這個數的機器數。機器數是帶符號的,在計算機用一個數的最高位存放符號, 正數爲0, 負數爲1.
真值:因爲第一位是符號位,所以機器數的形式值就不等於真正的數值。例如有符號數 10000011,其最高位1代表負,其真正數值是 -3 而不是形式值131(10000011轉換成十進制等於131)。所以,爲區別起見,將帶符號位的機器數對應的真正數值稱爲機器數的真值。
原碼:原碼就是符號位加上真值的絕對值, 即用第一位表示符號, 其餘位表示值.
反碼:反碼的表示方法是:正數的反碼是其本身,負數的反碼是在其原碼的基礎上, 符號位不變,其餘各個位取反.
補碼:補碼的表示方法是:正數的補碼就是其本身,負數的補碼是在其原碼的基礎上, 符號位不變, 其餘各位取反, 最後+1. (即在反碼的基礎上+1) - 庖丁解牛
既然知道了數據在計算機系統中是以補碼的形式存儲,那麼上面a[0]的在計算機系統中存儲的值呼之欲出,真值爲-1111111,原碼則爲11111111,反碼不多說,則其補碼則爲:10000001,所以第一個輸出a[0]爲-127是沒有問題的,然後就是說到&的情況,a[0] & 0xFF
也即是 10000001&11111111 其結果不言而喻 還是它自己本身 那麼爲什麼輸出的結果卻是129呢,原因在於,我們需要把byte類型轉爲int類型輸出,將a[0] 作爲int類型向控制檯輸出的時候,jvm作了一個補位的處理,因爲int類型是32位所以補位後的補碼就是1111111111111111111111111 10000001(32位),這個32位二進制補碼錶示的也是-127,發現沒有,雖然byte->int計算機背後存儲的二進制補碼由10000001(8位)轉化成了1111111111111111111111111 10000001(32位)很顯然這兩個補碼錶示的十進制數字依然是相同的。接下來纔是繼續和0xFF做&運算,即a[0]&0xff=1111111111111111111111111 10000001&11111111=000000000000000000000000 10000001 ,這個值算一下是129!
好,接下來看移位運算符,知道了補碼存儲之後,其實移位運算就是很顯然的事情了,這裏不再多說~
迴歸正題
其實,看完上面的Demo,大家其實心裏應該已經有了一點譜了,接着我們思考下,做byte->int的轉化,所有時候都只是爲了保持 十進制的一致性嗎?
不一定吧?好比這次項目拿到的文件流轉成byte數組,難道我們關心的是byte數組的十進制的值是多少嗎?我們關心的是其背後二進制存儲的補碼,所以大家應該能猜到爲什麼byte類型的數字要&0xff再賦值給int類型,其本質原因其實就是想保持二進制補碼的一致性。
當byte要轉化爲int的時候,高的24位必然會補1,這樣,其二進制補碼其實已經不一致了,&0xff可以將高的24位置爲0,低8位保持原樣。這樣做的目的就是爲了保證二進制數據的一致性。當然了,保證了二進制數據性的同時,如果二進制被當作byte和int來解讀,因爲符號位位置已經發生了變化,所以其10進制的值也必然是不同的。答疑
有人問爲什麼上面的式子中a[0]不是8位而是32位?這是因爲當系統檢測到byte可能會轉化成int或者說byte與int類型進行運算的時候,就會將byte的內存空間高位補1(也就是按符號位補位)擴充到32位,再參與運算。上面的0xff其實是int類型的字面量值,所以可以說byte與int進行運算。