为了深入了解Java的ClassLoader机制我们先来做以下实验
package javalang;
public class Test {
public static void main(String[] args) {
char[] c = toCharArray();
String s = new String( c);
}
}
String类有一个Package权限的构造函数String(int offset int length char[] array)按照默认的访问权限由于Test属于javalang包因此理论上应该可以访问String的这个构造函数编译通过!执行时结果如下
Exception in thread main javalangSecurityException:
Prohibited package name:
javalang
at javalangClassLoaderdefineClass(Unknown Source)
at javasecuritySecureClassLoaderdefineClass(Unknown Source)
at URLClassLoaderdefineClass(Unknown Source)
at URLClassLoaderaccess$(Unknown Source)
at URLClassLoader$run(Unknown Source)
at javasecurityAccessControllerdoPrivileged(Native Method)
at URLClassLoaderfindClass(Unknown Source)
at javalangClassLoaderloadClass(Unknown Source)
at sunmiscLauncher$AppClassLoaderloadClass(Unknown Source)
at javalangClassLoaderloadClass(Unknown Source)
at javalangClassLoaderloadClassInternal(Unknown Source)
奇怪吧?要弄清为什么会有SecurityException就必须搞清楚ClassLoader的机制
Java的ClassLoader就是用来动态装载class的ClassLoader对一个class只会装载一次JVM使用的ClassLoader一共有种
启动类装载器标准扩展类装载器类路径装载器和网络类装载器
这种ClassLoader的优先级依次从高到低使用所谓的双亲委派模型确切地说如果一个网络类装载器被请求装载一个javalangInteger它会首先把请求发送给上一级的类路径装载器如果返回已装载则网络类装载器将不会装载这个javalangInteger如果上一级的类路径装载器返回未装载它才会装载javalangInteger
类似的类路径装载器收到请求后(无论是直接请求装载还是下一级的ClassLoader上传的请求)它也会先把请求发送到上一级的标准扩展类装载器这样一层一层上传于是启动类装载器优先级最高如果它按照自己的方式找到了javalangInteger则下面的ClassLoader 都不能再装载javalangInteger尽管你自己写了一个javalangInteger试图取代核心库的javalangInteger是不可能的因为自己写的这个类根本无法被下层的ClassLoader装载
再说说Package权限Java语言规定在同一个包中的class如果没有修饰符默认为Package权限包内的class都可以访问但是这还不够准确确切的说只有由同一个ClassLoader装载的class才具有以上的Package权限比如启动类装载器装载了javalangString类路径装载器装载了我们自己写的javalangTest它们不能互相访问对方具有Package权限的方法这样就阻止了恶意代码访问核心类的Package权限方法