通常对于对象的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虚拟机》,由笔者验证得出。
在官网 http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6258289 描述了这一场景,并且叙述了这一理由。摘录如下:
I didn't understand the evaluation at first but it is correct. The key fact is that a runtime package is defined as the combination of the ClassLoader and the package name. That means that the classes in the example are indeed in different packages because classes from different ClassLoaders are always in different packages. This is specified in section 5.3 of the JVMS: http://java.sun.com/docs/books/jvms/second_edition/html/ConstantPool.doc.html If you have more than one ClassLoader then the only methods you can invoke on classes from other ClassLoaders are public ones because private, package and protected methods are all inaccessible due to being in a different package (even when their package has the same name).
简单翻译如下:
我最开始并不理解这种情况,虽然它是正确的。实际上一个运行的包它被定义为包名和classLoader的组合。意思即在上面的代码中,实际上已经存在不同的包了,因为它们是由不同的classLoader所加载的。详情见:JVM规范5.3。
如果存在这么一种情况,你可以访问指定类的公共属性,但不能访问私有,受保护的,以及类级别的方法或属性,那是因为他们本身就不是同一个包,尽管它们有相同的名字。
转载请标明出处:i flym
本文地址:https://www.iflym.com/index.php/code/jvm-constraint-for-default-or-protected-field-between-classloaders.html