上QQ阅读APP看书,第一时间看更新
解析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 访问标志解析器单元测试