java 类的加载过程

1 类的加载过程

用Java语言编写的源代码会被翻译成以class为后缀的文件,这种class文件是一种字节码文件。那么,JVM是如何把这种字节码文件加载进内存的呢?

JVM是通过一个称为ClassLoader东西来加载class文件的,每当JVM启动,它就会生成三个ClassLoader,它们分别是Bootstrap Loader,ExtClassLoader和AppClassLoader。这三个ClassLoader作用是不同的,它们所加载的class文件也是不同的。

(1)Bootstrap Loader是用C++语言实现的,它所加载的是JVM中最底层的类,它加载时的搜索路径是由sun.boot.class.path所指定的。

(2)ExtClassLoader是用来加载Java的一些库的,它加载时的搜索路径是由java.ext.dirs来决定的,该加载器在加载时不同于其他加载器,它加载时会搜索指定路径下的所有子目录,搜索java.ext.dirs所指定下的所有子目录下的class文件或jar文件。同时也可以用参数-Djava.ext.dirs来改变它的搜索路径。

(3)AppClassLoader也称System ClassLoader,它的搜索路径是由java.class.path来指定的,而且要注意AppClassLoader不会搜索java.class.path下的子目录的。

ExtClassLoader 和AppClassLoader在中只会存在一个实例,一旦运行了是无法改变它们的搜索路径的,也就是无法在程序中通过调用System.setProperty()来改变java.class.path或java.ext.dirs来改变它们的搜索路径的。

JVM加载class文件时采用了双亲委托模型,当需要加载一个class文件时,JVM会首先让这个加载器的父亲去加载(父类加载器指的是ExtClassLoader),如果父亲无法加载,JVM会让父亲的父亲去加载(父类的父类指的是BootstrapClassLoader),如果仍然无法加载,则让最初的加载器去加载(委托加载后如果都不成功就调用AppClassLoader加载)。这三个加载器的委托关系如图:

从上图可以看出,每当加载我们自己所编写的class文件时,首先会交给AppClassLoader的父亲ExtClassLoader去加载,如果它不能加载则会交给ExtClassLoader的父亲Bootstrp Loader去加载,如果仍无法加载的情况下则交给AppclassLoader加载。

在加载完Class文件之后,ClassLoader如何将class文件变成一个java类的。在ClassLoader类中有一个非常重要的方法叫findClass,它接收要加载的类的名字作为它的参数,在该方法里会找到class文件并读取文件中的内容到一个byte数组,然后再调用另外一个重要的方法defineClass,该方法能够将byte数组中的内容转化成一个相应的Class Object。defineClass方法的调用是通过JNI方式的,它的实现是用C++语言实现的本地化方法。


2 类加载方式

(1)显示加载发生在使用以下方法调用进行装载类的时候:

(1)ClassLoader.loadClass():使用指定的Classloader进行装载,结果返回一个类。

(2)Class.forName():使用当前类的Classloader进行装载,结果也是返回一个类。

(2)隐式类装载发生在由于引用、实例化或继承导致需要装载类的时候。

隐式类装载是在幕后启动的,JVM会解析必要的引用并装载类。使用new关键字创建实例。


3 类加载的委托机制的好处-安全性

JVM装载类使用的是“全盘负责委托机制”。

全盘负责,指当一个ClassLoader装载类的时候,除非显示的使用另外一个ClassLoader,该类所依赖和引用的其他类都由这个ClassLoader载入。

委托机制,首先委托父装载器寻找目标类,只有在找不到的情况下才从自己的类路径中查找并装载类,这一点是从安全角度考虑的,试想如果有人编写了一个恶意的基础类,比如String类,并装载到JVM中将会引起多么可怕的后果呢。但是,由于有了全盘负责委托机制,String类永远是由根装载器装载,这样就避免了事件的发生。另外,我们经常遇到的NoSuchMethodError的错误往往就是因为加载了不同版本的包造成的。


public class Person {
	static 
	{
		System.out.println("I am loading");
	}
}
public class Test {
	    public static void main(String[] args) {
		Person p = new Person ();
		Class pclass = p.getClass();
		ClassLoader cl = pclass.getClassLoader();
		System.out.println("person类加载器"+cl);
		System.out.println("person类加载器的父类加载器"+cl.getParent());
		System.out.println("person类加载器的祖父类加载器"+cl.getParent().getParent());
	}
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值