1. 程式人生 > >byte[i] & 0xFF原因(byte為什麼要與上0xFF?)

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