byte為什么要與上0xff(轉)


 

無意間翻看之間的代碼,發現了一段難以理解的代碼。

復制代碼
     byte[] bs = digest.digest(origin.getBytes(Charset.forName(charsetName))) ;  
          
        for (int i = 0; i < bs.length; i++) {  
            int c = bs[i] & 0xFF ;
            if(c < 16){ 
                sb.append("0");  
            }  
            sb.append(Integer.toHexString(c)) ;  
        }  
        return sb.toString() ;  
復制代碼

bs是由一段字符串經過MD5加密后,輸出的byte數組。我起初難以理解為什么在接下來的循環中要將bs[i]&oxFF再復制給int類型呢?

bs[i]是8位二進制,0xFF轉化成8位二進制就是11111111,那么bs[i]&0xFF不是還是bs[i]本身嗎?有意思嗎?

 

后來我又寫了一個demo

復制代碼
package jvmProject;

public class Test {

    public static void main(String[] args) {
        byte[] a = new byte[10];
        a[0]= -127;
        System.out.println(a[0]);
        int c = a[0]&0xff;
        System.out.println(c);
    }
}
復制代碼

我先打印a[0],在打印a[0]&0xff后的值,本來我想結果應該都是-127.

但是結果真的是出人意料啊!

-127

129

到底是為什么呢?&0xff反而不對了。

 

樓主真的是不懂啊,后來往補碼那個方向想了想。

記得在學計算機原理的時候,了解到計算機內的存儲都是利用二進制的補碼進行存儲的。

復習一下,原碼反碼補碼這三個概念

對於正數(00000001)原碼來說,首位表示符號位,反碼 補碼都是本身

對於負數(100000001)原碼來說,反碼是對原碼除了符號位之外作取反運算即(111111110),補碼是對反碼作+1運算即(111111111)

概念就這么簡單。

 

當將-127賦值給a[0]時候,a[0]作為一個byte類型,其計算機存儲的補碼是10000001(8位)。

將a[0] 作為int類型向控制台輸出的時候,jvm作了一個補位的處理,因為int類型是32位所以補位后的補碼就是1111111111111111111111111 10000001(32位),這個32位二進制補碼表示的也是-127.

發現沒有,雖然byte->int計算機背后存儲的二進制補碼由10000001(8位)轉化成了1111111111111111111111111 10000001(32位)很顯然這兩個補碼表示的十進制數字依然是相同的。

 

但是我做byte->int的轉化 所有時候都只是為了保持 十進制的一致性嗎?

不一定吧?好比我們拿到的文件流轉成byte數組,難道我們關心的是byte數組的十進制的值是多少嗎?我們關心的是其背后二進制存儲的補碼吧

所以大家應該能猜到為什么byte類型的數字要&0xff再賦值給int類型,其本質原因就是想保持二進制補碼的一致性。

當byte要轉化為int的時候,高的24位必然會補1,這樣,其二進制補碼其實已經不一致了,&0xff可以將高的24位置為0,低8位保持原樣。這樣做的目的就是為了保證二進制數據的一致性。

當然拉,保證了二進制數據性的同時,如果二進制被當作byte和int來解讀,其10進制的值必然是不同的,因為符號位位置已經發生了變化。

 

象例2中,int c = a[0]&0xff;  a[0]&0xff=1111111111111111111111111 10000001&11111111=000000000000000000000000 10000001 ,這個值算一下就是129,

所以c的輸出的值就是129。有人問為什么上面的式子中a[0]不是8位而是32位,因為當系統檢測到byte可能會轉化成int或者說byte經過一些運算后會轉化成int時,就會將byte的內存空間高位補1擴充到32位,再參與運算。

 

其實是從數字類型擴展到較寬的類型時,補零擴展還是補符號位擴展。
這是因為Java中只有有符號數,當byte擴展到short, int時,即正數都一樣,因為為符號位是0,所以無論如何都是補零擴展;但負數補零擴展和按符號位擴展結果完全不同。
補符號數,原數值不變。
補零時,相當於把有符號數看成無符號數,比如-127 = 0x81,看成無符號數就是129, 256 + (- 127) 
對於有符號數,從小擴展大時,需要用&0xff這樣方式來確保是按補零擴展。
而從大向小處理,符號位自動無效,所以不用處理。

 

讓我理解更深了,也就是說在byte向int擴展的時候,自動轉型是按符號位擴展的,這樣子能保證十進制的數值不會變化,而&0xff是補0擴展的,這樣子能保證二進制存儲的一致性,但是十進制數值已經發生變化了。也就是說按符號位擴展能保證十進制數值不變,補0擴展能保證二進制存儲不會變。而正數可以說是既按符號位擴展,又是補0擴展,所以在二進制存儲和十進制數值上都能保證一致。

 

http://www.cnblogs.com/think-in-java/p/5527389.html

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM