通常对于对象的default及protected访问限制,一般的说法即为只能由同包或子类才能访问。对于同包的类,是可以访问default及protected的字段的。但如下情况除外:
如果一个由其它途径加载的类尝试访问同一个包中由其它加载器加载的类的受保护或默认级别字段时,将产生一个IllegalAccessError错误,即不允许访问指定的字段。
即只有相同加载器之间才可以访问受保护字段。
验证程序如下所示:类Foo
public class Foo implements IFoo {
protected int i = 2;
}
类Foo2将要访问Foo:
public class Foo2 {
public static void redFoo() {
Foo f = new Foo();
System.out.println(f.i);
}
}
以下为执行验证的代码:
MyClassLoader2 myClassLoader2 = new MyClassLoader2(T.class.getClassLoader());
Class<?> clazz = myClassLoader2.loadClass("m_ylf.study.java.classLoad.Foo2");
// Class<?> clazz2 = myClassLoader2.loadClass("m_ylf.study.java.classLoad.Foo");
clazz.getDeclaredMethod("redFoo").invoke(null);
在以上代码中,将由类加载器单独加载Foo2,而Foo仍由appClassLoader来加载。上面的程序即通过调用Foo2的redFoo方法来读取Foo类的受保护字段,并输出信息。执行结果如下所示:
Exception in thread "main" java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
......
Caused by: java.lang.IllegalAccessError: tried to access field com.m_ylf.study.java.classLoad.Foo.i from class com.m_ylf.study.java.classLoad.Foo2
at com.m_ylf.study.java.classLoad.Foo2.redFoo(Foo2.java:8)
... 11 more
即不能访问该字段,需要注意到的是,在以上的代码中,我们专门注释了使用myClassLoader加载Foo的代码。如果我们取消注释,即也叫myClassLoader来加载Foo,那么结果即能够正确的输出相对应的数字(具体输出结果就不再列出,可以单独测试)。
需要注意的是,如果Foo中的字段i,本身就是public类型的,则该错误也不会发生。即这个约束只限制在default以及protected字段上。
这也是为了将一个恶意的代码,加载到虚拟机,来尝试访问本来不能够被访问的信息,如标准java API中的受保护信息(标准API是由java 系统加载器来加载的)。
以上的叙述取自《深入JVM虚拟机》,由笔者验证得出。
继续阅读“JVM对于不同classLoader加载的对象之间default或protected字段的访问限制”