📚《深入浅出JVM字节码》
《Java虚拟机字节码:从入门到实战》的开源版本。作者通过自己的实战经验,整合出一套适合新手的高效学习教程。归纳并提炼知识点,制定合理路线,帮助读者更快掌握核心技术。
Class文件结构中的访问标志项access_flags是用U2类型存储的,也就是2个字节。用某个bit位的值是否为1判断该类或接口的访问权限、属性。
访问标志与类或接口的访问权限、属性的映射
标志名 | 值 | 描述 |
---|---|---|
ACC_PUBLIC | 0x0001 | 声明为public |
ACC_FINAL | 0x0010 | 声明为final,不允许继承 |
ACC_SUPER | 0x0020 | Jdk1.0.2之后编译的class文件都会有此值 |
ACC_INTERFACE | 0x0200 | 声明该class是接口 |
ACC_ABSTRACT | 0x0400 | 声明为抽象类 |
ACC_SYNTHETIC | 0x1000 | 表示该class文件并非由java代码编译生成 |
ACC_ANNOTATION | 0x2000 | 标志这是一个注解类型 |
ACC_ENUM | 0x4000 | 标志这是一个枚举类型 |
如何判断一个类设置了上表中的哪些标志呢?
首先从Class文件字节缓存中读取到access_flags的值,再将access_flags转为int类型,将转换后的值“算术与(&)”上各个标志的值,判断结果是否等于这个标志的值,实现代码如下。
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文件中紧挨着常量池存储的就是access_flags,如下图所示。
现在我们来实现class文件访问标志解析器AccessFlagsHandler,并将AccessFlagsHandler解析器交给ClassFileAnalysiser管理。AccessFlagsHandler的排序值设置为3,即放在常量池解析器之后,约定在常量池解析器解析完成之后再到访问标志解析器解析。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方法将访问标志输出为字符串。
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));
}
}
单元测试结果如下图所示。
发布于:2021 年 07 月 24 日
作者: 吴就业
链接: https://github.com/wujiuye/JVMByteCodeGitBook
来源: Github Pages 开源电子书《深入浅出JVM字节码》(《Java虚拟机字节码从入门到实战》的第二版),未经作者许可,禁止转载!
📚目录
订阅
订阅新文章发布通知吧,不错过精彩内容!
输入邮箱,提交后我们会给您发送一封邮件,您需点击邮件中的链接完成订阅设置。