黑马程序员---java类加载器

                                                  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

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值