一 . 面向接口编程. 不要面向类编程. 二 . 关于异常: 常,但是不能抛出比父类方法抛出的异常级别更高的异常. 三 . Java的类装载器(Class Loader)和命名空间(NameSpace) 1.摘要: 空间,运行时包等概念,同时讨论一些在学习中容易混淆的问题。 2.类装载器的功能及分类: (bootstrap)和用户自定义装载器(user-defined class loader)。 bootstrap是JVM自带的类装载器,用来装载核心类库,如java.lang.*等。由例1可以看出,java.lang.Object 是由bootstrap装载的。 Java提供了抽象类ClassLoader,所有用户自定义类装载器都实例化自ClassLoader的子类。 情况下默认装载用户类。系统类装载器可以通过ClassLoader.getSystemClassLoader() 方法得到。 例1,测试你所使用的JVM的ClassLoader try c = Class.forName(“LoaderSample1”); 在我的机器上(Sun Java 1.4.2)的运行结果 第一行表示,系统类装载器实例化自类sun.misc.Launcher$AppClassLoader
求装载某个类时,它首先委托自己的parent去装载,若parent能装载,则返回这个类所对应的Class对象,若 parent不能装载,则由parent的请求者去装载。 如图1所示,loader2的parent为loader1,loader1的parent为system class loader。假设loader2被要求装载 类MyClass,在双亲委派模型下,loader2首先请求loader1代为装载,loader1再请求系统类装载器去装载 MyClass。若系统装载器能成功装载,则将MyClass所对应的Class对象的reference返回给loader1,loader1再 将reference返回给loader2,从而成功将类MyClass装载进虚拟机。若系统类装载器不能装载MyClass,loader1 会尝试装载MyClass,若loader1也不能成功装载,loader2会尝试装载。若所有的parent及loader2本身都不能 装载,则装载失败。 若有一个能成功装载,实际装载的类装载器被称为定义类装载器,所有能成功返回Class对象的装载器(包括定 义类装载器)被称为初始类装载器。如图1所示,假设loader1实际装载了MyClass,则loader1为MyClass的定义 类装载器,loader2和loader1为MyClass的初始类装载器。
需要指出的是,Class Loader是对象,它的父子关系和类的父子关系没有任何关系。一对父子loader可能实例 化自同一个Class,也可能不是,甚至父loader实例化自子类,子loader实例化自父类。假设MyClassLoader继 承自ParentClassLoader,我们可以有如下父子loader: 那么双亲委托模型为什么更安全了?因为在此模型下用户自定义的类装载器不可能装载应该由父亲装载器装载 的可靠类,从而防止不可靠甚至恶意的代码代替由父亲装载器装载的可靠代码。实际上,类装载器的编写者可 以自由选择不用把请求委托给parent,但正如上所说,会带来安全的问题。【原理:限制了自定义装载器的权利】
类是不可见的,但只要得到类所对应的Class对象的reference,还是可以访问另一命名空间的类。 LoaderSample3由自定义的装载器loader负责装载,两个类不在同一命名空间,但LoaderSample2得到了 LoaderSample3所对应的Class对象的reference,所以它可以访问LoaderSampl3中公共的成员(如age)。 例2不同命名空间的类的访问 public class LoaderSample2
编译:javac LoaderSample2.java; javac sub/LoaderSample3.java 从运行结果中可以看出,在类LoaderSample2中可以创建处于另一命名空间的类LoaderSample3中的对象并可以 访问其公共成员age。 5.运行时包(runtime package):
由同一类装载器定义装载的属于相同包的类组成了运行时包,决定两个类是不是属于同一个运行时包,不仅要 看它们的包名是否相同,还要看的定义类装载器是否相同。只有属于同一运行时包的类才能互相访问包可见的 类和成员。这样的限制避免了用户自己的代码冒充核心类库的类访问核心类库包可见成员的情况。假设用户自 己定义了一个类java.lang.Yes,并用用户自定义的类装载器装载,由于java.lang.Yes和核心类库java.lang.* 由不同的装载器装载,它们属于不同的运行时包,所以java.lang.Yes不能访问核心类库java.lang中类的包可 见的成员。
。命名空间并没有完全禁止属于不同空间的类的互相访问,双亲委托模型加强了Java的安全,运行时包增加了 对包可见成员的保护。
四 . 再谈类装载器 作为程序的一部分,每个类都有一个class对象.换言之,每当你编写并且编译了一个新类,就会产生一个 Class对象(更恰当的说,是被保存在一个同名的.class文件中).在运行期,一旦我们想生成这个类的一个对象,运 行这个程序的java虚拟机(JVM)首先检查这个类的Class对象是否已经加载,如果尚未加载,JVM就会根据类名查找 .class文件,并将期载入,所以Java程序并不是一开始执行就被完全加载的,这一点与许多传统语言不同,一旦某 个类的Class对象被载入内存,它就被用来创建这个类的所有对象,请看下面的例子[需要时才加载,不需要则不加载]: class Candy class Gum class Cookie public class ClassTest 打印结果如下: 这里的每个类Candy,Gum,Cookie中,都有一个static语句,在类第一次被加载时执行,这时会有相应结果打印出来 告诉我们这个类什么时候被加载了.在main()中,创建对象的代码被置于打印语句之间,以帮助我们判断加载的时 间点.你可以从输出中看出,Class对象仅在需要的时候才被加载,static语句块是在类加载时被执行的.看这行 :Class.forName("Gum"),这是Class类(所有Class对象都属于这个类型)的一个static成员,Class对象和其他对 象一样,我们可以获取并操作它的引用(这就是类加载器的工作),forName()时取得Class对象的引用一个方法,它 时用一个包含目标类的文件名的String作为输入参数,返回一个Class对象的引用,上面的代码忽略了返回值,对 forName()的调用是为了它产生的"副作用":如果类Gum还没有被加载就加载它.在加载的过程中,Gum的static语 句被执行.在前面的例子中,如果Class.forName()找不到你要加载的类.它会抛出异常ClassNotFoundException 加载类的时机【也就是加载class对象的时机】静态成员调用,class.forName方法调用,new方法(等同于一个静态方法)调用
Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1118975 [收藏到我的网摘] [发送Trackback] senton发表于 2006年08月25日 18:36:00
|
|