java类加载器
---------------------- android培训、java培训、期待与您交流! ----------------------
今天学到了java类加载器,这在实际开发中用的比较多,我觉得这是以后在实际开发中应用比较多的
看完视频后于是总结一下:
一.java中默认有三种类加载器:引导类加载器,扩展类加载器,系统类加载器(也叫应用类加载器)
类加载器是Java最强大的特征之一。
1.引导类加载器仅仅负责加载核心的Java库,
比如位于<JAVA_HOME>/jre/lib 目录下的vm.jar,core.jar。这个类加载器,是JVM核心部分,是用native代码写成的。
2. 扩展类加载器负责加载扩展路径下的代码,
一般位于<JAVA_HOME>/jre/lib/ext 或者通过java.ext.dirs 这个系统属性指定的路径下的代码。
3.应用程序类加载器负责加载java.class.path(映射系统参数 CLASSPATH的值) 路径下面的代码,
这个类加载器是由 sun.misc.Launcher$AppClassLoader 实现的。
当处理类加载器时,父委托模式是一个需要理解的关键概念。它规定:类加载器在加载自己的类之前,
可以委托先加载父类。父类加载器可以是客户化的类加载器或者引导类加载器。但是有一点很重要,
类加载器只能委托自己的父类加载器,而不能是子类加载器(只能向上不能向下)。
图为类加载器层次图
如果应用程序类加载器需要加载一个类,它首先委托扩展类加载器,扩展类加载器再委托引导类加载器。如果父类加载器不能加载类,子类加载器就回在自己的库中查找这个类。基于这个特性,类加载器只负责它的祖先无法加载的类。
如 果类加载器加载一个类,这个类不是在类加载器树上的叶子节点上,就会出现一些有趣的问题。比如例12-1,一个名为WhichClassLoader1 的类加载了一个名为WhichClassLoader2类,WhichClassLoader2又调用了名为WhichClassLoader3的类。
WhichClassLoader1 和 WhichClassLoader2 源代码
public class WhichClassLoader1 {
public static void main(String[] args) throws javax.naming.NamingException
{
// Get classpath values
String bootClassPath = System.getProperty("sun.boot.class.path");
String extClassPath = System.getProperty("java.ext.dirs");
String appClassPath = System.getProperty("java.class.path");
// Print them out
System.out.println("Bootstrap classpath =" + bootClassPath + "/n");
System.out.println("Extensions classpath =" + extClassPath + "/n");
System.out.println("Application classpath=" + appClassPath + "/n");
// Load classes
Object bj = new Object();
WhichClassLoader1 wcl1 = new WhichClassLoader1();
WhichClassLoader2 wcl2 = new WhichClassLoader2();
// Who loaded what?
System.out.println("Object was loaded by "
+ obj.getClass().getClassLoader());
System.out.println("WCL1 was loaded by "
+ wcl1.getClass().getClassLoader());
System.out.println("WCL2 was loaded by "
+ wcl2.getClass().getClassLoader());
wcl2.getTheClass();
}
}
======================================================================
public class WhichClassLoader2 {
// This method is invoked from WhichClassLoader1
public void getTheClass() {
WhichClassLoader3 wcl3 = new WhichClassLoader3();
System.out.println("WCL3 was loaded by "
+ wcl3.getClass().getClassLoader());
}
}
如 果所有的WhichClassLoaderX 类都放在应用程序的类路径下,三个类就会被应用程序类加载器加载,这个例子就会运行正常。现在假定把WhichClassLoader2 类文件打包成JAR文件放在<JAVA_HOME>/jre/lib/ext 目录下,运行WhichClassLoader1,就会看到例下面的输出:
例NoClassDefFoundError 异常跟踪
Bootstrap classpath
=C:/WebSphere/AppServer/java/jre/lib/vm.jar;C:/WebSphere/AppServer/java/jre/lib
/core.jar;C:/WebSphere/AppServer/java/jre/lib/charsets.jar;C:/WebSphere/AppServ
er/java/jre/lib/graphics.jar;C:/WebSphere/AppServer/java/jre/lib/security.jar;C
:/WebSphere/AppServer/java/jre/lib/ibmpkcs.jar;C:/WebSphere/AppServer/java/jre/
lib/ibmorb.jar;C:/WebSphere/AppServer/java/jre/lib/ibmcfw.jar;C:/WebSphere/AppS
erver/java/jre/lib/ibmorbapi.jar;C:/WebSphere/AppServer/java/jre/lib/ibmjcefw.j
ar;C:/WebSphere/AppServer/java/jre/lib/ibmjgssprovider.jar;C:/WebSphere/AppServ
er/java/jre/lib/ibmjsseprovider2.jar;C:/WebSphere/AppServer/java/jre/lib/ibmjaa
slm.jar;C:/WebSphere/AppServer/java/jre/lib/ibmjaasactivelm.jar;C:/WebSphere/Ap
pServer/java/jre/lib/ibmcertpathprovider.jar;C:/WebSphere/AppServer/java/jre/li
b/server.jar;C:/WebSphere/AppServer/java/jre/lib/xml.jar
Extensions classpath =C:/WebSphere/AppServer/java/jre/lib/ext
Application classpath=.
Exception in thread "main" java.lang.NoClassDefFoundError: WhichClassLoader3
at java.lang.J9VMInternals.verifyImpl(Native Method)
at java.lang.J9VMInternals.verify(J9VMInternals.java:59)
at java.lang.J9VMInternals.initialize(J9VMInternals.java:120)
at WhichClassLoader1.main(WhichClassLoader1.java:17)
正 如所看到的,由于WhichClassLoader3 在应用程序类路径下,程序失败,收到一个NoClassDefFoundError 的异常,这看起来有些奇怪。
问题在于:它现在在一个错误的类路径下面。当WhichClassLoader2被扩展类加载器加载的时候,发生了什么呢?实 际上,应用程序类加载器委托扩展类加载器加载WhichClassLoader2,扩展类加载器又委托引导类加载器。由于引导类加载器找不到这个类,类加 载的控制就会返回给扩展类加载器。扩展类加载器在自己的路径下找到了这个类将它加载。现在,当一个类已经被类加载器加载后,这个类需要的任何其他的新类都 必须用同一个类加载器加载他们(或者遵循父委托模式,由父类加载器加载)。
所以当WhichClassLoader2 需要访问WhichClassLoader3 的时候,扩展类加载器就会获得这个请求去加载WhichClassLoader3,扩展类加载器先委托引导类加载器,但是引导类加载器找不到这个类,于是 扩展类加载器便试图装入自身但是也找不到这个类,原因是WhichClassLoader3不在扩展类路径而是在应用程序类路径。由于扩展类加载器无法委 托应用程序类加载器,所以就会出现NoClassDefFoundError 的异常。
二.Java 程序包含很多类文件,每一个都与单个Java类相对应,这些类文件不像静态C程序,一次性加载入内存,
它们随时需要随时加载。这就是类加载器与众不同的地 方。
它从源文件(通常是.class 或 .jar文件)获得不依赖平台的字节码,
然后将它们加载到JVM内存空间,所以它们能被解释和执行。
默认状态下,应用程序的每个类由 java.lang.ClassLoader加载。因为它可以被继承,所以可以自由地加强其功能。
三. 使用自定义类加载器的原因(重点,也就是我们为什么要用这个类加载器呢?)
默认的 java.lang.ClassLoader仅仅可以从加载本地文件系统的类。
Java被设计成不论本地磁盘或网络都有足够的弹性加载类,并且可以在加载 之前处理特殊事物。
例如:应用程序可以检查Web站点或FTP上插入类的更新版本并且自动校验数字签名确保执行可信任的代码。
许多众所周知的软件都使用自 己的类加载器。
通常默认加载器是所谓的bootstrap类加载器;
它是负责加载诸如java.lang.Object等关键类和加 载其他rt.jar文件的运行时代码到内存。
因为Java语言规范没有提供bootstrap类加载器的详细信息,不同的JVM可能有不同的类加载器。
如果看到网页上有applets在运行,则它使用的是自定义类加载器。
嵌入到浏览器中的applet阅读器包含了可以访问远程服务器上站点的类加载器,
它可以通过HTTP加载原始字节码文件,并且在JVM中将它们转换成类。
四.类加载器要点
类加载器(除了bootstrap类加载器)有父类加载器,这些父类是基本加载器的加载器实例。
最重要的一点是设置正确的父加载器。然后可以使用 类加载器的getParent()方法实现委派类请求
(例如:自定义类加载器找不到使用专门方法的类时)。
此时必须为将父加载器作为 java.lang.ClassLoader构造器的参数:
public class MyClassLoader extends ClassLoader
{
public MyClassLoader()
{
super(MyClassLoader.class.getClassLoader());
}
}
loadClass(String name)方法是ClassLoader的入口。名字参数是完全资格类名(FQCN),
例如关于包类名。
如果父加载器设置正确,当请求 MyClassLoader中的loadClass(String name)方法加载类,但又找不到需要加载的类时,
则首先会询问父加载器。如果父加载器也找不到此类,则调用findClass(String name)方法。
默认状态下findClass(String name)会抛出ClassNotFoundException例外,
自定义类加载器的开发者都希望从 java.lang.ClassLoader继承时跳过这个方法。
findClass()方法的目标是为MyClassLoader容纳所有专门代码,
此时不需要重复其他代码(例如当加载失败时调用系统 ClassLoader)。
在此方法中,ClassLoader需要从原文件中获取字节码。
一旦找到字节码则会调用defineClass()方法。 ClassLoader实例调用此方法是非常重要的。
因此,如果两个ClassLoader实例定义了来自不同或相同原文件的字节码,则被定义的类也将区 别对待。
我们给出两个相似的类加载器MyClassLoader1 和 MyClassLoader2,
它们都可以从相同的源文件找到MyCoolClass字节码。
如果一个程序通过这两个加载器分别独立加载 MyCoolClass实例
(coolClass1通过MyClassLoader1加载, coolClass2通过MyClassLoader2加载),
MyCoolClass.class能够被独立定义。执行下面的代码:
MyCoolClass coolClass1 = (MyCoolClass)coolClass2;
将得到一个ClassCastException例外。因为它们是不同的加载器 所定义的,
JVM将它们看成不同的类。虽然它们是相同类型的类并且从相同的源文件加载,
但是变量coolClass1和coolClass2是不兼容的。
不论是否跳过findClass() 或 loadClass(),
getSystemClassLoader()方法将以实际ClassLoader对象的形式直接访问系统 ClassLoader。
也可以通过调用findSystemClass(String name)方法间接访问。getParent()方法允许获得父加载器。
Listing A给出了可以运行的自定义类加载器示例。
---------------------- android培训、java培训、期待与您交流! ----------------------
详细请查看:http://edu.csdn.net/heima