虚拟机执行子系统_类加载器、双亲委派模型

类加载器的定义

通过一个类的全限定名来获取描述此类的二进制字节流这个动作放到java虚拟机外部实现,以便让应用程序自己决定如何去获取所需要的类。实现这个动作的代码称为类加载器

类和类加载器

对于任意一个类,都需要由加载它的类加载器和这个类本身一同确定在java虚拟机中的唯一性,每一个类加载器,都拥有一个独立的类名称空间。

也就是:比较两个类是否相等,只有在这两个类都是同一个类加载器加载的并且是来源于同一个Class文件才会相等,否则,就算两个类来源于同一个Class文件,被同一个虚拟机加载,只要加载它们的类加载器不同,那这两个类就必定不相等

这里的相等:包括类的Class对象的equals方法,isAssignableFrom,isInstance方法的返回结果。也会影响instanceof对象从属关系的判断

双亲委派模型

从java虚拟机角度来看,存在两种不同的累加载器:一种是启动类加载器,另一种是所有其他的类加载器,这些类加载器都由java语言实现,独立于虚拟机外部,并且全部都继承自抽象类java.lang.ClassLoader

java类加载器细分:

* 启动类加载器(Bootstrap ClassLoader) : 负责将存放在<JAVA_HOME>\lib目录中,或者被-Xbootclasspath参数指定的路径中,并且是虚拟机识别(仅按照文件名识别,如rt.jar,名字不符合的类库)类库加载到虚拟机内存中。

启动类加载器无法被java程序直接引用,用户在编写自定义类加载器时候,需要把加载请求委派给引导类加载器,那直接使用null代替就可

* 扩展类加载器(Extension ClassLoader): 这个加载器由sun.misc.Laucher$ExtClassLoader实现,负责加载<JAVA_HOME>\lib\ext目录中的,或者被java.ext.dirs系统变量所指定的路径中的所有类库,开发者可以直接使用扩展类加载器

查看java.ext.dirs系统变量指定的路径:

public static void main(String[] args) {
		System.out.println(System.getProperty("java.ext.dirs"));
	}

输出: C:\Program Files\Java\jdk1.7.0_79\jre\lib\ext;C:\Windows\Sun\Java\lib\ext

*  应用程序类加载器(Application ClassLoader):这个类加载器有sun.misc.Launcher$App-ClassLoader实现。由于这个类加载器是ClassLoader中的getSystemClassLoader()方法的返回值,所以一般也称为系统类加载器,负责加载用户类路径ClassPath上的指定的类库,开发者可用直接使用,一般情况下,这个加载器就是程序中默认的类加载器

public static void main(String[] args) {
		System.out.println(ClassLoader.getSystemClassLoader());
	}

输出:sun.misc.Launcher$AppClassLoader@19b1de

验证Application ClassLoader的父加载器是扩展类加载器

public static void main(String[] args) {
		System.out.println(ClassLoader.getSystemClassLoader().getParent());
	}

输出:sun.misc.Launcher$ExtClassLoader@19b1de

再往上,扩展类加载器的父类应该是启动类加载器

public static void main(String[] args) {
		System.out.println(ClassLoader.getSystemClassLoader().getParent().getParent());
	}

结果输出:null ,为什么是null?

因为Application Classloader和Extension ClassLoader都是java实现的,在java堆中都存在一个实例,而启动类加载器BootStrap ClassLoader是c实现的,在java堆没有实例,所以打印为空

java程序一般都是由这三种类加载器互相配合加载的,可以加入自定义的类加载器,这些加载器的关系一般如图:


图中展示的类加载器之间的层次关系,称为类加载器的双亲委派模型,这些类加载器除了顶层的没有父类加载器,下面都存在父类加载器

双亲加载器的工作流程:

1. 如果一个类加载器接受到一个加载请求,它首先不会自己去加载这个类,而是把这个请求委派给父类加载器去加载,每一个层次的加载器都是如此

2. 只有父加载器无法完成这次加载(在它的搜索范围内,找不到这个类),才会给子类加载器去加载

双亲委派模型来组织类加载器使得类加载器之间具备了一种层次关系,不管去加载什么类,都会优先去调用类加载器,比如java.lang.Object是在rt.jar中的,这样不管在哪个环境中,加载器产生的Object.class都是同一个类,如果自己编写一个Object类放在classpath下面,一个环境中存在两个不同的Object.class,java程序最基本的行为无法保证,程序将一片混乱

即使重新定义rt.jar下的Object类,编译可以通过,但是永远不会去加载,即使自定义加载器加载也会抛异常


猜你喜欢

转载自blog.csdn.net/ditto_zhou/article/details/79960224