byte[i] & 0xFF原因(byte爲什麼要與上0xFF?)

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進行運算。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章