彷佛你總也記不住,byte取值範圍是 -127~128 仍是 -128~127

小傅哥 | bugstack.cn
沉澱、分享、成長,專一於原創專題案例,以最易學習編程的方式分享知識,讓本身和他人都能有所收穫。目前已完成的專題有;Netty4.x實戰專題案例、用Java實現JVM、基於JavaAgent的全鏈路監控、手寫RPC框架、架構設計專題案例、源碼分析等。

你用劍🗡、我用刀🔪,好的代碼都很燒,望你不吝出招!java

1、前言介紹

不管在面試過程當中仍是平時的技術交流中,彷佛有不少小夥伴始終記不住java中byte類型的取值範圍是多少。究其緣由大部分程序員對這個取值範圍是不在乎的,由於知道與不知道都不影響你完成工做。另外這種知識點壓根不是讓你死記硬背的,固然若是你是從其餘文科專業轉過來學編程開發的,還情有可原。但對一個理科生來講,就不太應該了。程序員

2、取值範圍計算

在java中,byte佔1個字節,8比特位,能夠想象成8個小塊的數據區間,首位用0、1表明符號位。0[正]、1[負],那麼繪製出一個表格以下;面試

byte
序號 8 7 6 5 4 3 2 1
2ⁿ 2^7 2^6 2^5 2^4 2^3 2^2 2^1 2^0
128 64 32 16 8 4 2 1
+127 0 1 1 1 1 1 1 1
-128 1 0 0 0 0 0 0 0

+127 二進制求和編程

2^0+2^1+2^2+2^3+2^4+2^5+2^6+2^7
= 2^(n+1) - 1
= 127
複製代碼

-128 二進制求和數組

2^8
= 128
複製代碼

好了,如今看懂邏輯就很清晰了,爲何是負數到-128,由於1表明負數的符號位,也就整整好好是2的8次方,-128。架構

3、進制數值轉換

由於java語言與一些其餘語言byte的取值範圍不一樣,因此在有時候處理一些文件時候須要進行進制轉換。也就是 -128~127 與 0~255 的轉換處理;框架

好比咱們如今將一個java中byte=120,轉換成 0~255取值範圍的數值;jvm

通常能夠進行與運算;源碼分析

120 & 0x0FF
複製代碼

同時還能夠進行增位運算;(也就是將8個字節長度的內容,放到16個長度中,進行轉換)學習

byte[] val = {-120};
BigInteger bigInteger = new BigInteger(1, val);
//有符號
System.out.println(bigInteger.byteValue());
//無符號(增位)
String str_hex = bigInteger.toString(16);
System.out.println(Integer.parseInt(str_hex, 16)); // 136
複製代碼

4、解析一段class字節碼

java的類文件都會被編譯成class文件,那麼class文件須要通過jvm的解析、驗證,加載等處理才能夠被虛擬機的指令執行操做。

若是下是一段class文件的byte數組,將內容解析出對應的結果;

public class ClassReaderTest {

    //取部分字節碼:java.lang.String
    private static byte[] classData = {
            -54, -2, -70, -66, 0, 0, 0, 52, 2, 26, 3, 0, 0, -40, 0, 3, 0, 0, -37, -1, 3, 0, 0, -33, -1, 3, 0, 1, 0, 0, 8, 0,
            59, 8, 0, 83, 8, 0, 86, 8, 0, 87, 8, 0, 110, 8, 0, -83, 8, 0, -77, 8, 0, -49, 8, 0, -47, 1, 0, 3, 40, 41, 73, 1,
            0, 20, 40, 41, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 79, 98, 106, 101, 99, 116, 59, 1, 0, 20, 40, 41,
            76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 59, 1, 0, 3, 40, 41, 86, 1, 0, 3,
            40, 41, 90, 1, 0, 4, 40, 41, 91, 66, 1, 0, 4, 40, 41, 91, 67, 1, 0, 4, 40, 67, 41, 67, 1, 0, 21, 40, 68, 41, 76,
            106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 59, 1, 0, 4, 40, 73, 41, 67, 1, 0, 4};

    public static void main(String[] args) {

        //classData是咱們的字節碼,第一是-54,由於byte取值範圍是-128~+127,因此若是想看到和其餘虛擬機同樣的值,須要進行與運算。
        System.out.println("* byte字節碼與運算原值(-54)換行後(-54 & 0x0FF):" + (-54 & 0x0FF));

        //校驗魔數
        readAndCheckMagic();

        //校驗版本號
        readAndCheckVersion();

        //接下來會依次讀取[能夠參照java版本虛擬機代碼];constantPool、accessFlags、thisClassIdx、supperClassIdx、interfaces、fields、methods、attributes
    }

    /** * 校驗魔數 * <p> * 不少文件格式都會規定知足該格式的文件必須以某幾個固定字節開頭,這幾個字節主要起到標識做用,叫做魔數(magic number)。 * 例如; * PDF文件以4字節「%PDF」(0x2五、0x50、0x4四、0x46)開頭, * ZIP文件以2字節「PK」(0x50、0x4B)開頭 * class文件以4字節「0xCAFEBABE」開頭 */
    private static void readAndCheckMagic() {
        System.out.println("\r\n------------ 校驗魔數 ------------");
        //從class字節碼中讀取前四位
        byte[] magic_byte = new byte[4];
        System.arraycopy(classData, 0, magic_byte, 0, 4);

        //將4位byte字節轉成16進制字符串
        String magic_hex_str = new BigInteger(1, magic_byte).toString(16);
        System.out.println("magic_hex_str:" + magic_hex_str);

        //byte_magic_str 是16進制的字符串,cafebabe,由於java中沒有無符號整型,因此若是想要無符號只能放到更高位中
        long magic_unsigned_int32 = Long.parseLong(magic_hex_str, 16);
        System.out.println("magic_unsigned_int32:" + magic_unsigned_int32);

        //魔數比對,一種經過字符串比對,另一種使用假設的無符號16進制比較。若是使用無符號比較須要將0xCAFEBABE & 0x0FFFFFFFFL與運算
        System.out.println("0xCAFEBABE & 0x0FFFFFFFFL:" + (0xCAFEBABE & 0x0FFFFFFFFL));

        if (magic_unsigned_int32 == (0xCAFEBABE & 0x0FFFFFFFFL)) {
            System.out.println("class字節碼魔數無符號16進制數值一致校驗經過");
        } else {
            System.out.println("class字節碼魔數無符號16進制數值一致校驗拒絕");
        }

    }

    /** * 校驗版本號 * <p> * 魔數以後是class文件的次版本號和主版本號,都是u2類型。假設某class文件的主版本號是M,次版本號是m,那麼完整的版本號能夠 * 表示成「M.m」的形式。次版本號只在J2SE 1.2以前用過,從1.2開始基本上就沒有什麼用了(都是0)。主版本號在J2SE 1.2以前是45, * 從1.2開始,每次有大版本的Java版本發佈,都會加1{4五、4六、4七、4八、4九、50、5一、52} */
    private static void readAndCheckVersion() {
        System.out.println("\r\n------------ 校驗版本號 ------------");

        //從class字節碼第4位開始讀取,讀取2位
        byte[] minor_byte = new byte[2];
        System.arraycopy(classData, 4, minor_byte, 0, 2);
        //將2位byte字節轉成16進制字符串
        String minor_hex_str = new BigInteger(1, minor_byte).toString(16);
        System.out.println("minor_hex_str:" + minor_hex_str);
        //minor_unsigned_int32 轉成無符號16進制
        int minor_unsigned_int32 = Integer.parseInt(minor_hex_str, 16);
        System.out.println("minor_unsigned_int32:" + minor_unsigned_int32);

        //從class字節碼第6位開始讀取,讀取2位
        byte[] major_byte = new byte[2];
        System.arraycopy(classData, 6, major_byte, 0, 2);
        //將2位byte字節轉成16進制字符串
        String major_hex_str = new BigInteger(1, major_byte).toString(16);
        System.out.println("major_hex_str:" + major_hex_str);
        //major_unsigned_int32 轉成無符號16進制
        int major_unsigned_int32 = Integer.parseInt(major_hex_str, 16);
        System.out.println("major_unsigned_int32:" + major_unsigned_int32);

        System.out.println("版本號:" + major_unsigned_int32 + "." + minor_unsigned_int32);

    }

}
複製代碼

測試結果:

* byte字節碼與運算原值(-54)換行後(-54 & 0x0FF):202

------------ 校驗魔數 ------------
magic_hex_str:cafebabe
magic_unsigned_int32:3405691582
0xCAFEBABE & 0x0FFFFFFFFL3405691582
class字節碼魔數無符號16進制數值一致校驗經過 ------------ 校驗版本號 ------------ minor_hex_str:0 minor_unsigned_int32:0 major_hex_str:34 major_unsigned_int32:52 版本號:52.0 Process finished with exit code 0 複製代碼

5、綜上總結

  • 關於byte在文章;《用java實現jvm虛擬機》中講過,可是沒有單獨拿出來分析,如今單獨分析下也加強記憶。
  • 任何一個可能不起眼的知識點,不是他不重要,而是你尚未用到。就像有句話說,不是讀書沒用,而是你沒用。國語博大精深!
  • 認認真真對待每個知識點,不斷的夯實本身的地基,這就像是蓋房子在打地基同樣。越深越穩,最終所服能於你的上層架構纔會更加精進。

6、文末驚喜

小傅哥 | 沉澱、分享、成長,讓本身和他人都能有所收穫!

相關文章
相關標籤/搜索