您的位置:首页 > 职场人生

黑马程序员--05.类加载器--04【应用程序默认类加载器】【验证类加载器委托机制】

2013-08-17 09:12 751 查看

类加载器--4

应用程序默认类加载器

验证类加载器委托机制

----------- android培训java培训、java学习型技术博客、期待与您交流! ------------

1.    应用程序的默认类加载器

前面的日志主要阐述了某一个类加载器如何进行对指定类的加载。现在就要讨论一下当JVM真正执行用户的应用程序代码的时候,到底从哪一个类加载器开始对指定的类进行加载的。

1). 线程上下文类加载器

(1). Thread类的成员变量 contextClassLoader

[1]. private ClassLoader
contextClassLoader;


[2]. 表示当前线程执行到的代码中涉及到的类哪一个类加载器来加载。

(2). 操作成员变量contextClassLoader

[1]. 获取当前线程上下文类加载器

publicClassLoader getContextClassLoader();

[2]. 为当前线程设置上下文类加载器

public voidsetContextClassLoader(ClassLoader paramClassLoader);

注意】Thread类是一种JavaBean类,所以对应的字段对应的SetterGetter

2). Launcher简述

(1). Launcher类的类加载器为Bootstrap类加载器

[1]. Launcher类位于sun.misc这个包下面

[2]. Launcher.class文件位于jre/lib下的rt.jar内部,所以Launcher.class由Bootstrap类加载器进行加载

(2). Launcher的部分源码



(3). 从Launcher构造方法看默认类加载器和线程上下文类加载器的关系

[1]. Bootstrap对Launcher的初始化

执行java命令的时候,JVM会先使用Bootstrap类加载器载入初始化一个Launcher实例,这样就会调用Launcher的构造方法

[2]. 首先JVM获取了ExtClassLoader的实例

[3]. 然后JVM向getAppClassLoader()传入ExtClassLoader的实例并获取到AppClassLoader的实例

【注意】以上就是把AppClassLoaderparent属性设置为ExtClassLoader实例构建默认类加载器的组织关系

[4]. 最后JVM通过setContextClassLoader()将当前线程类加载器设置为AppClassLoader

结论】因此AppClassLoader就是应用程序默认的类加载器

结论AppClassLoaderExtClassLoaderBootstrap类加载器通过调用Launcher的构造方法进行加载的

2.    验证类加载器委托机制

(1). Class类的getClassLoader() 方法

[1]. 功能:获取加载这个类或者接口的类加载器

[2]. 方法原型public ClassLoader getClassLoader();

[3]. 返回值:返回加载这个类或者接口类加载器

注意】如果这个类是由Bootstrap类加载器进行加载的,则getClassLoader()方法返回值就用null代表Bootstrap类加载器。

(2). 场景模拟

目标】在AppClassLoaderExtClassLoader加载管辖范围构建重名的字节码文件






[1]. 直接运行ClassLoaderTest.java的结果

sun.misc.Launcher$AppClassLoader@c4fe76
null
sun.misc.Launcher$AppClassLoader@c4fe76
【结果说明】

{1}. sun.misc.Launcher$AppClassLoader@c4fe76 说明加载ClassLoaderTest.class的类加载器是AppClassLoader类加载器

{2}. null说明加载System.class的类加载器是Bootstrap类加载器

【用null表示Bootstrap类加载器】

{3}.
sun.misc.Launcher$AppClassLoader@c4fe76说明当前线程的上下文类加载器是AppClassLoader类加载器

[2]. 将上面图中的ClassLoader工程打包成ClassLoader.jar文件并放置到jre/lib/ext子目录下【这个文件夹下的jar中的class文件是ExtClassLoader的管辖范围】。

这样在CLASSPATH指定的当前MyEclipse的ClassLoader工程中存在ClassLoaderTest.class字节码文件【AppCLassLoader管辖范围】



AppClassLoader和ExtClassLoader管辖的范围内出现了重名的ClassLoaderTest.class字节码文件。

[3]. 再次运行ClassLoaderTest.class结果

sun.misc.Launcher$ExtClassLoader@11e1e67
null
sun.misc.Launcher$AppClassLoader@c4fe76
【结果说明】

{1}. sun.misc.Launcher$ExtClassLoader@11e1e67 说明加载ClassLoaderTest.class的类加载器是ExtClassLoader类加载器

{2}. null说明加载System.class的类加载器仍然是Bootstrap类加载器

{3}. sun.misc.Launcher$AppClassLoader@c4fe76说明当前线程的上下文类加载器是AppClassLoader类加载器

(2). 结果分析

[1]. 当jre/lib/ext下面没有ClassLoaderTest.jar时候,打印结果是

sun.misc.Launcher$AppClassLoader@c4fe76
null
sun.misc.Launcher$AppClassLoader@c4fe76

{1}. 这个运行过程相当于:xxxx>java ClassLoaderTest

{2}. java命令后面字节码ClassLoaderTest第一个要被加载到内存的类

{3}. JVM启动时会指示Bootstrap类加载器调用Launcher的构造方法设置当前线程的类加载器为AppClassLoader

此时JVM就要求AppClassLoader类加载器去加载java命令后ClassLoaderTest

{4}. 由于AppClassLoader采用双亲委托机制对类ClassLoaderTest进行加载

{4}1. 因此AppClassLoader将ClassLaderTest交给ExtClassLoader这个类加载器去加载。ClassLoaderTest将这个类交给Bootstrap类加载器进行加载。但是BootStrap和ExtClassLoader这两个类加载器都在指定的范围内找不到ClassLoaderTest.class这个文件,所以最后由AppClassLoader类加载器到工程的bin下面去查找这个类,并加载成功。

{4}2. 这样JVM指示主线程运行AppClassLoader找到的ClassLoaderTest这个类主方法

运行结果表明

{1}.加载ClassLoaderTest这个类的类加载器是AppClassLoader去做的,所以返回的是AppClassLoader这个类加载器

{2}.加载System这个类的类加载器是Bootstrap去做的

{3}.当前线程使用的默认类加载器就是AppClassLoader

[2]. 当jre/lib/ext下面有ClassLoaderTest.jar时候,打印结果是

sun.misc.Launcher$ExtClassLoader@11e1e67
null
sun.misc.Launcher$AppClassLoader@c4fe76

{1}. JVM启动当前线程的类加载器AppClassLoader去加载main方法所在的类ClassLoaderTest

{2}. AppClassLoader采用双亲委托机制将加载任务丢给ExtClassLoader。ExtClassLoader也采取同样的措施将这个任务交给Bootstrap类加载器。但是Bootstrap没找到这个类名。就将任务转回ExtClassLoader。但是ExtClassLoader却在自己的管辖范围找到了ClassLoaderTest字节码文件。这个时候ClassLoaderTest类的加载就完成了。

{3}. 所以JVM运行main方法的是被正确加载并成功解析的jre/lib/ext中的ClassLoader.jar中的ClassLoaderTest类,而不是MyEclipse中的ClassLoader工程下的bin中的ClassLoaderTest这个类

{4}. JVM执行的就是jre/lib/ext中的ClassLoader.jarClassLoaderTest类。

sun.misc.Launcher$ExtClassLoader@11e1e67

{4}1. System.out.println(ClassLoaderTest.class.getClassLoader());

由于ClassLoaderTest被ExtClassLoader类加载器进行加载,所以getClassLoader()就返回加载这个类的类加载器 -----ExtClassLoader

null

{4}2. System.out.println(System.class.getClassLoader());

由于System类被Bootstrap类加载器进行加载,所以getClassLoader()就返回加载这个类的类加载器 ----- Bootstrap

sun.misc.Launcher$AppClassLoader@c4fe76

{4}3. System.out.println(Thread.currentThread().getContextClassLoader())

JVM启动调用Bootstrap初始化其他的类加载器,这个Bootstrap实际上是在实例化Launcher类的过程中,在Launcher类的构造方法中当前线程的上下文类加载器设置为AppClassLoader。所以这个打印返回时AppClassLoader。

 
现象解释

修改工程ClassLoader下的ClassLoaderTest.java并在MyEclipse中点击“RUN”运行修改后的ClassLoaderTest.class文件,但是运行结果仍然是

sun.misc.Launcher$ExtClassLoader@11e1e67
null
sun.misc.Launcher$AppClassLoader@c4fe76

不变。

{1}. 点击MyEclipse中“RUN”之后,相当于【之前的日志对MyEclipse对应的CMD格式有讨论过】

xxxx\ClassLoader\bin > java ClassLoaderTest

这样:在当前线程的类加载器AppClassLoader对ClassLoaderTest进行查找并加载的过程,ExtClassLoader就会率先在自己的管辖范围(jre/lib/ext)
里面找到这个ClassLoaderTest类

{2}. JVM就会执行第一个被加载并解析好的CLassLoaderTest类中的main方法。但是MyEclipse中ClassLoader工程下的ClassLoaderTest.class实际上并没有加载到,那这个类就不能被使用。那么这个类中的main方法就不会被执行

{3}. 由于修改的是没有被加载的源文件,所以一直不能被运行!!

【JVM加载的第一个类】

JVM要加载的第一个类不是main方法中遇到的第一个类而是java命令后面第一个类名。(也就是main方法所在的类是JVM第一个要加载的类
)

问题】为什么JVM会第一个去加载java命令后面的类名而不是第一个去加载main中遇到的第一个类名

{1}. 在类的生命周期中JVM明确规定这个类必须先被加载并正确解析之后才能使用

{2}. Java中一个main方法一定是属于某一个类。所以要运行这个main方法,就必须使得这个类可以被使用,那就要求这个类已经先被夹在并解析成功

【JVM执行main方法

{1}. JVM第一次找到是哪一个类哪一个类中的main方法就被执行

{2}. 第一个被JVM加载的类就是java命令后面的第一个类名
(等价于:要执行的main方法所在的类名)

【注意】想使用一个类中成员必须这个类被加载并正确解析之后才能使用。调用某个类的main方法也不例外。

----------- android培训java培训、java学习型技术博客、期待与您交流! ------------
 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: