public class ClassReaderTest {
//取部分字節(jié)碼: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是我們的字節(jié)碼,第一是-54,因為byte取值范圍是-128~+127,所以如果想看到和其他虛擬機(jī)一樣的值,需要進(jìn)行與運算。
System.out.println("* byte字節(jié)碼與運算原值(-54)換行后(-54 & 0x0FF):" + (-54 & 0x0FF));
//校驗?zāi)?shù)
readAndCheckMagic();
//校驗版本號
readAndCheckVersion();
//接下來會依次讀取[可以參照java版本虛擬機(jī)代碼];constantPool、accessFlags、thisClassIdx、supperClassIdx、interfaces、fields、methods、attributes
}
/**
* 校驗?zāi)?shù)
* <p>
* 很多文件格式都會規(guī)定滿足該格式的文件必須以某幾個固定字節(jié)開頭,這幾個字節(jié)主要起到標(biāo)識作用,叫作魔數(shù)(magic number)。
* 例如;
* PDF文件以4字節(jié)“%PDF”(0x25、0x50、0x44、0x46)開頭,
* ZIP文件以2字節(jié)“PK”(0x50、0x4B)開頭
* class文件以4字節(jié)“0xCAFEBABE”開頭
*/
private static void readAndCheckMagic() {
System.out.println("\r\n------------ 校驗?zāi)?shù) ------------");
//從class字節(jié)碼中讀取前四位
byte[] magic_byte = new byte[4];
System.arraycopy(classData, 0, magic_byte, 0, 4);
//將4位byte字節(jié)轉(zhuǎn)成16進(jìn)制字符串
String magic_hex_str = new BigInteger(1, magic_byte).toString(16);
System.out.println("magic_hex_str:" + magic_hex_str);
//byte_magic_str 是16進(jìn)制的字符串,cafebabe,因為java中沒有無符號整型,所以如果想要無符號只能放到更高位中
long magic_unsigned_int32 = Long.parseLong(magic_hex_str, 16);
System.out.println("magic_unsigned_int32:" + magic_unsigned_int32);
//魔數(shù)比對,一種通過字符串比對,另外一種使用假設(shè)的無符號16進(jìn)制比較。如果使用無符號比較需要將0xCAFEBABE & 0x0FFFFFFFFL與運算
System.out.println("0xCAFEBABE & 0x0FFFFFFFFL:" + (0xCAFEBABE & 0x0FFFFFFFFL));
if (magic_unsigned_int32 == (0xCAFEBABE & 0x0FFFFFFFFL)) {
System.out.println("class字節(jié)碼魔數(shù)無符號16進(jìn)制數(shù)值一致校驗通過");
} else {
System.out.println("class字節(jié)碼魔數(shù)無符號16進(jìn)制數(shù)值一致校驗拒絕");
}
}
/**
* 校驗版本號
* <p>
* 魔數(shù)之后是class文件的次版本號和主版本號,都是u2類型。假設(shè)某class文件的主版本號是M,次版本號是m,那么完整的版本號可以
* 表示成“M.m”的形式。次版本號只在J2SE 1.2之前用過,從1.2開始基本上就沒有什么用了(都是0)。主版本號在J2SE 1.2之前是45,
* 從1.2開始,每次有大版本的Java版本發(fā)布,都會加1{45、46、47、48、49、50、51、52}
*/
private static void readAndCheckVersion() {
System.out.println("\r\n------------ 校驗版本號 ------------");
//從class字節(jié)碼第4位開始讀取,讀取2位
byte[] minor_byte = new byte[2];
System.arraycopy(classData, 4, minor_byte, 0, 2);
//將2位byte字節(jié)轉(zhuǎn)成16進(jìn)制字符串
String minor_hex_str = new BigInteger(1, minor_byte).toString(16);
System.out.println("minor_hex_str:" + minor_hex_str);
//minor_unsigned_int32 轉(zhuǎn)成無符號16進(jìn)制
int minor_unsigned_int32 = Integer.parseInt(minor_hex_str, 16);
System.out.println("minor_unsigned_int32:" + minor_unsigned_int32);
//從class字節(jié)碼第6位開始讀取,讀取2位
byte[] major_byte = new byte[2];
System.arraycopy(classData, 6, major_byte, 0, 2);
//將2位byte字節(jié)轉(zhuǎn)成16進(jìn)制字符串
String major_hex_str = new BigInteger(1, major_byte).toString(16);
System.out.println("major_hex_str:" + major_hex_str);
//major_unsigned_int32 轉(zhuǎn)成無符號16進(jìn)制
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);
}
}