1. 程式人生 > >你真的搞明白Java中基本資料型別的轉換了嗎?

你真的搞明白Java中基本資料型別的轉換了嗎?

寫這篇部落格源於在進行長連線通訊的時候我們需要將流資料和我們的String、基本型別的資料之間進行轉換,我們知道byte[]與String之間的轉換相當方便,那麼接下來我們就要弄懂byte[]與基本資料型別之間的轉換了。

計算機中的儲存

首先,本文講的東西並不侷限於Java,每種語言雖然基本資料型別的長度不同,但是計算機儲存的原理都是一樣的。我們知道,不管是什麼語言,資料都有” 原碼”,” 反碼”,” 補碼”三種形式,”原碼”是我們能看懂的編碼方式,而為了解決使用原碼帶來的一些問題,計算機內部採用”補碼”的形式儲存資料。先來說說”補碼”儲存的原理:
1.如果是正數,補碼與原碼相同;
2.如果是負數,補碼則是先對原碼的絕對值按位取反,再加1。

我們看兩個例子:

10: 原碼: 00001010
反碼: 00001010
補碼: 00001010

-10:原碼:10001010
反碼: 11110101
補碼: 11110110

所以十進位制10在計算機中的儲存格式為00001010,而十進位制-10的儲存格式為11110110。

不同格式的轉換

規則1:少位數 —> 多位數:我們在進行資料型別轉換時,對資料做補齊的操作,保證資料位數發生改變時其值依然不變。正數的在其左邊補0,負數則是在其左邊補1,而無符號的則也是在其左邊補0。

規則2:多位數 —> 少位數:如果我們需要想將多位數轉換為少位數,則只需對資料攔截低位數的長度即可。

來看個例子:

byte b = (byte) -1;
char c = (char) b;
int i = (int) c;

int(32位) –> byte(8位):-1預設為32位的int型別,儲存為0xffffffff,轉換為8位的byte型別則執行規則2攔截低8位,所以b儲存型別為的0xff,即十進位制的-1;

byte(8位) –> char(16位):byte為8位,轉換為char型別時執行規則1,在其左邊補齊8個1,則c儲存資料為0xffff,而char為無符號的16位,十進位制對應的是65535;

char(16位) –> int(32位):char為16位的無符號型別,執行規則1,向左邊補齊16個0,則i的值為0x0000ffff,對應十進位制的65535。

移位規則

  1. 當發生左移(<<)時,右邊補0;
  2. 當發生有符號的右移(>>)時,左邊補符號位,但是符號位不變。
  3. 當發生無符號的右移(>>>)時,無論正負,左邊補0。

int與byte[]轉換

有了上面的基礎我們就可以看看在長連線通訊時最為常見的int與byte[]之間是怎麼轉換的。

public static byte[] int2byte(int res) {
    byte[] targets = new byte[4];
    // 最低位 
    targets[0] = (byte) (res & 0xff);
    // 次低位 
    targets[1] = (byte) ((res >> 8) & 0xff);
    // 次高位 
    targets[2] = (byte) ((res >> 16) & 0xff);
    // 最高位,無符號右移。 
    targets[3] = (byte) (res >>> 24);
    return targets; 
} 

上面的方法是將一個int型別的資料儲存在了一個大小為4的byte[]裡,由於int型別為32位,而byte為8位,則需要32 / 8 = 4個byte單元來儲存。

我們看到好多都與0xff進行了與操作,其實這個與操作在位數不同轉化時尤為有效,我們來分析一下上面的程式碼。

targets[0]是將32位的int型與0xff進行與運算時,編譯器會自動將0xff擴充套件到32位0x000000ff,這樣與操作之後高24位全部清零,只留下了低8位,然後轉化為byte時,剛好只攔截了低8位。後面3個都是同理,只是先將要攔截的那8位移到了低8位。值得一提的是,有符號右移(>>)時補齊的是符號位,而無符號右移(>>>)時補齊的則直接是0。

上面可能有疑惑:0xff擴充套件到32位為什麼不是0xffffffff呢?我們來看看下面的定義:

   int a = 0xff;
   int b = 0xffffffff;

我們列印a和b的值我們發現,a的十進位制位255,b的十進位制為-1。a儲存的是正數,當然是0補齊啦。

接下來我們再看看將byte[]轉換為int的方法

public static int byte2int(byte[] res) { 

    int targets = (res[0] & 0xff) | ((res[1] << 8) & 0xff00)
    | ((res[2] << 24) >>> 8) | (res[3] << 24); 
    return targets; 
} 

res[0]與0xff進行與運算,首先編譯器會將res[0]轉化為int型別,而0xff會被補齊為32位,即0x000000ff,與0xff進行與運算後則雖已為32位,但是隻有低8位數字有效,其餘全部被置零。

res[1]則是先左移8位,使res[1]的資料移到次低8位,在與0xff00進行與運算使次低8位有效,其餘清零。

res[2]則是先左移24位,使res[2]置於高8位,其餘位置零,再無符號右移,使次8位有效。

res[3]則是直接左移24位,高8位有效,其餘置零。

最後進行或運算,將不同的byte單元合併到int型中來。

理解了上面說的這麼多,我們再也不必擔心繁雜的邏輯運算了。