我在理解Java中内部类的访问标志(特别是私有)的使用时遇到了问题.我在字节代码中找到的标志似乎与反射API提供的信息不一致.
我附上以下程序来说明问题.该程序有一个私有内部类,并使用三种不同的方法进行自我分析:
令人惊讶的是,这给出了不同的结果,这是输出:
inner class is private (inspection): false
inner class is private (reflection): true
inner class is private (ASM): false
Run Code Online (Sandbox Code Playgroud)
有谁知道这里发生了什么?下面是复制问题的代码,我在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();
}
}
Run Code Online (Sandbox Code Playgroud)
首先,你的第一个方法实际上并没有检查任何东西,它只是显示一个常量的假.所以真正的问题是为什么后两种方法给出不同的结果.
要了解实际情况,我们可以从编译测试类开始
public class TestModifiers {
// this is the class to be tested
private class InnerClass {}
}
Run Code Online (Sandbox Code Playgroud)
拆卸TestModifiers$InnerClass.class
给出
.version 51 0
.source TestModifiers.java
.class super TestModifiers$InnerClass
.super java/lang/Object
.inner private InnerClass TestModifiers$InnerClass TestModifiers
.field synthetic final this$0 LTestModifiers;
.method private <init> : (LTestModifiers;)V
; method code size: 10 bytes
.limit stack 2
.limit locals 2
aload_0
aload_1
putfield TestModifiers$InnerClass this$0 LTestModifiers;
aload_0
invokespecial java/lang/Object <init> ()V
return
.end method
Run Code Online (Sandbox Code Playgroud)
您可能会注意到,类文件没有private
在访问标志中设置标志(它拥有的唯一标志是super
,为所有普通类设置).这并不奇怪,因为ACC_PRIVATE实际上不是有效的类文件访问标志(JVMS8,第71页).因此,当您通过ASM检查类文件访问标志时,您自然会得到错误的结果.
但是,该类确实具有一个InnerClasses
属性,并且此属性具有private
其访问标志,因为ACC_PRIVATE是内部类属性的有效访问标志(JVMS8,第116页).
是不是java.lang.Class.getModifiers()从内部类属性获取数据?好吧,检查有点棘手.该方法是本机方法.在此处检查源显示它调用JVM_GetClassModifiers.这包含在头文件jvm.h中,其中包含一个有趣的注释.
/* Differs from JVM_GetClassModifiers in treatment of inner classes.
This returns the access flags for the class as specified in the
class file rather than searching the InnerClasses attribute (if
present) to find the source-level access flags. Only the values of
the low 13 bits (i.e., a mask of 0x1FFF) are guaranteed to be
valid. */
JNIEXPORT jint JNICALL
JVM_GetClassAccessFlags(JNIEnv *env, jclass cls);
Run Code Online (Sandbox Code Playgroud)
所以你有它.JVM_GetClassModifiers实际上检查了InnerClasses属性,而JVM_GetClassAccessFlags仅检查类文件访问标志,这与您对ASM的操作相同.
归档时间: |
|
查看次数: |
511 次 |
最近记录: |