图 1 parent delegation模型
需要指出的是,class loader是对象,它的父子关系和类的父子关系没有任何关系。一对父子loader可能实例化自同一个class,也可能不是,甚至父loader实例化自子类,子loader实例化自父类。假设myclassloader继承自parentclassloader,我们可以有如下父子loader:
classloader loader1 = new myclassloader();
//参数 loader1 为 parent
classloader loader2 = new parentclassloader(loader1);
那么parent delegation模型为什么更安全了?因为在此模型下用户自定义的类装载器不可能装载应该由父亲装载器装载的可靠类,从而防止不可靠甚至恶意的代码代替由父亲装载器装载的可靠代码。实际上,类装载器的编写者可以自由选择不用把请求委托给parent,但正如上所说,会带来安全的问题。
命名空间及其作用
每个类装载器有自己的命名空间,命名空间由所有以此装载器为初始类装载器的类组成。不同命名空间的两个类是不可见的,但只要得到类所对应的class对象的reference,还是可以访问另一命名空间的类。
例2演示了一个命名空间的类如何使用另一命名空间的类。在例子中,loadersample2由系统类装载器装载,loadersample3由自定义的装载器loader负责装载,两个类不在同一命名空间,但loadersample2得到了loadersample3所对应的class对象的reference,所以它可以访问loadersampl3中公共的成员(如age)。
例2 不同命名空间的类的访问
/*loadersample2.java*/import java.net.*;import java.lang.reflect.*;public class loadersample2 { public static void main(string[] args) { try { string path = system.getproperty("user.dir"); url[] us = {new url("file://" + path + "/sub/")}; classloader loader = new urlclassloader(us); class c = loader.loadclass("loadersample3"); object o = c.newinstance(); field f = c.getfield("age"); int age = f.getint(o); system.out.println("age is " + age); } catch (exception e) { e.printstacktrace(); } }
}
/*sub/loadersample3.java*/public class loadersample3 { static { system.out.println("loadersample3 loaded"); } public int age = 30;}
编译:
javac loadersample2.java;
javac sub/loadersample3.java
运行:java loadersample2
loadersample3 loaded
age is 30
从运行结果中可以看出,在类loadersample2中可以创建处于另一命名空间的类loadersample3中的对象并可以访问其公共成员age。
运行时包(runtime package)
由同一类装载器定义装载的属于相同包的类组成了运行时包,决定两个类是不是属于同一个运行时包,不仅要看它们的包名是否相同,还要看类装载器是否相同。只有属于同一运行时包的类才能互相访问包可见的类和成员。这样的限制避免了用户自己的代码冒充核心类库的类访问核心类库包可见成员的情况。假设用户自己定义了一个类java.lang.yes,并用用户自定义的类装载器装载,由于java.lang.yes和核心类库java.lang.*由不同的装载器装载,它们属于不同的运行时包,所以java.lang.yes不能访问核心类库java.lang中类的包可见的成员。
总结
在简单讨论了类装载器,parent delegation模型,命名空间,运行时包后,相信大家已经对它们的作用有了一定的了解。命名空间并没有完全禁止属于不同空间的类的互相访问,双亲委托模型加强了java的安全,运行时包增加了对包可见成员的保护。
2
您可能感兴趣的文章:
如您对本文有疑问或者有任何想说的,请点击进行留言回复,万千网友为您解惑!
网友评论