- Java虛擬機字節碼:從入門到實戰
- 吳就業
- 658字
- 2021-01-08 19:08:26
解析class文件的訪問標志
Class文件結構中的訪問標志項access_flags是用U2類型存儲的,也就是2個字節。用某個bit位的值是否為1判斷該類或接口的訪問權限、屬性。訪問標志與類或接口的訪問權限、屬性的映射如表2-33所示。
表2-33 類訪問權限和屬性修飾符標志

如何判斷一個類設置了表2-33中的哪些標志呢?首先從Class文件字節緩存中讀取到access_flags的值,再將access_flags轉為int類型,將轉換后的值“算術與”上各個標志的值,判斷結果是否等于這個標志的值。如代碼清單2-34所示。
代碼清單2-34 獲取Class文件的access_flags字符串表示
public class ClassAccessFlagUtils { private static final Map<Integer, String> classAccessFlagMap = new HashMap<>(); static { // 公有類型 classAccessFlagMap.put(0x0001, "public"); // 不允許有子類 classAccessFlagMap.put(0x0010, "final"); classAccessFlagMap.put(0x0020, "super"); // 接口 classAccessFlagMap.put(0x0200, "interface"); // 抽象類 classAccessFlagMap.put(0x0400, "abstract"); // 該class非java代碼編譯后生成 classAccessFlagMap.put(0x1000, "synthetic"); // 注解類型 classAccessFlagMap.put(0x2000, "annotation"); // 枚舉類型 classAccessFlagMap.put(0x4000, "enum"); } /** * 獲取16進制對應的訪問標志字符串表示 (僅用于類的訪問標志) * * @param flag訪問標志 * @return */ public static String toClassAccessFlagsString(U2 flag) { final int flagVlaue = flag.toInt(); StringBuilder flagBuild = new StringBuilder(); classAccessFlagMap.keySet() .forEach(key -> { if ((flagVlaue & key) == key) { flagBuild.append(classAccessFlagMap.get(key)).append(","); } }); return flagBuild.length() > 0 && flagBuild.charAt(flagBuild.length() - 1) == ',' ? flagBuild.substring(0, flagBuild.length() - 1) : flagBuild.toString(); } }
現在我們來實現class文件訪問標志解析器AccessFlagsHandler,并將AccessFlagsHandler解析器交給ClassFileAnalysiser管理。AccessFlagsHandler的排序值設置為3,即放在常量池解析器之后,約定在常量池解析器解析完成之后再到訪問標志解析器解析。AccessFlagsHandler的實現如代碼清單2-35所示。
代碼清單2-35 AccessFlagsHandler解析器
public class AccessFlagsHandler implements BaseByteCodeHandler { @Override public int order() { return 3; } @Override public void read(ByteBuffer codeBuf, ClassFile classFile) throws Exception { classFile.setAccess_flags(new U2(codeBuf.get(), codeBuf.get())); } }
最后編寫單元測試,驗證class文件訪問標志解析器是否能正常完成解析。在單元測試中,調用ClassAccessFlagUtils工具類的toClassAccessFlagsString方法將訪問標志輸出為字符串。如代碼清單2-36所示。
代碼清單2-36 訪問標志解析器單元測試
public class AccessFlagsHandlerTest { @Test public void testAccessFlagsHandlerHandler() throws Exception { ByteBuffer codeBuf = ClassFileAnalysisMain.readFile("RecursionAlgorithmMain.class"); ClassFile classFile = ClassFileAnalysiser.analysis(codeBuf); // 獲取訪問標志 U2 accessFlags = classFile.getAccess_flags(); // 輸出為字符串 System.out.println(ClassAccessFlagUtils.toClassAccessFlagsString(accessFlags)); } }
單元測試輸出結果如圖2.5所示。

圖2.5 訪問標志解析器單元測試