Java规范中私有内部类的jvm访问标志与反射API不一致?
我在理解Java内部类的访问标志(尤其是私有)的使用时遇到了问题。我在字节码中找到的标志似乎与反射API提供的信息不一致
我附上以下程序来说明这个问题。该程序有一个私有的内部类,并使用三种不同的方法进行分析:
- 使用JClassLib检查类文件,并将访问标志与来自JVM Spec的def进行比较
- 使用反射API
- 使用ASM 5.0
令人惊讶的是,这会给出不同的结果,这是输出:
inner class is private (inspection): false
inner class is private (reflection): true
inner class is private (ASM): false
有人知道这是怎么回事吗?下面是复制问题的代码,我在Mac上使用了JRE build 1.8.0_05-b13来运行这个程序
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.Opcodes;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.lang.reflect.Modifier;
public class TestModifiers {
// this is the class to be tested
private class InnerClass {}
public static void main(String[] args) throws Exception {
// check whether class is private using inspection, and comparison with standard
int flags = 0x0020; // inspect class file using JClassLib
int private_flag = 0x0002; // acc to http://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.6-300-D.1-D.1
System.out.println("inner class is private (inspection): " + ((flags & private_flag) == private_flag));
// check whether class is private using reflection
Class inner = InnerClass.class;
System.out.println("inner class is private (reflection): " + Modifier.isPrivate(inner.getModifiers()));
// now try to do the same by reading byte code using ASM
String PATH_TO_CLASSFILES = "<replace by path to class file>";
File classFile = new File(PATH_TO_CLASSFILES+"TestModifiers$InnerClass.class");
InputStream in = new FileInputStream(classFile);
class Visitor extends ClassVisitor {
public Visitor() {
super(Opcodes.ASM5);
}
@Override
public void visit(final int version, final int access, final String name,final String signature, final String superName,final String[] interfaces) {
boolean isPrivate = ((access & Opcodes.ACC_PRIVATE) == Opcodes.ACC_PRIVATE);
System.out.println("inner class is private (ASM): " + isPrivate);
}
}
new ClassReader(in).accept(new Visitor(), 0);
in.close();
}
}
# 1 楼答案
首先,您的第一个方法实际上并没有检查任何东西,它只是显示一个常量false。所以真正的问题是为什么后两种方法给出不同的结果
要了解实际情况,我们可以从编译测试类开始
分解
TestModifiers$InnerClass.class
给出正如您可能注意到的,类文件没有在访问标志中设置
private
标志(它唯一的标志是super
,这是为所有普通类设置的)。这并不奇怪,因为ACC_PRIVATE实际上不是有效的类文件访问标志(JVM8,第71页)。因此,当您通过ASM检查类文件访问标志时,自然会得到错误的结果然而,该类确实有一个
InnerClasses
属性,并且该属性在其访问标志中有private
,因为ACC_PRIVATE是内部类属性的有效访问标志(JVM8,第116页)可能是java。朗,同学们。getModifiers()是否从内部类属性获取数据?嗯,检查起来有点棘手。该方法是本地方法Checking the source here显示它调用JVM_GetClassModifiers。这包含在标题jvm.h中,其中包含一条有趣的注释
好了。JVM_GetClassModifiers实际上会检查InnerClasses属性,而JVM_GetClassAccessFlags只检查类文件访问标志,这与ASM相当