初探类加载器
阅读目录
引言
在学习类加载器之前,咱们先看一段代码,然后试着分析最后的结果是什么
package classloader.qihuiqiang.com; class Singleton { private static Singleton singleton=new Singleton(); public static int count1; public static int count2=0; private Singleton() { count1++; count2++; } public static Singleton GetInstance() { return singleton; } } public class classloader { public static void main(String[] args) { Singleton singleton=Singleton.GetInstance(); System.out.println(singleton.count1); System.out.println(singleton.count2); } }
上面的代码的答案是多少呢?经过运行最后出现了以下答案
1 0
但是如果把上面粗体的代码换个位置,换成以下的情况,在猜猜,运行的答案是多少呢
package classloader.qihuiqiang.com; class Singleton { public static int count1; public static int count2=0; private static Singleton singleton=new Singleton(); private Singleton() { count1++; count2++; } public static Singleton GetInstance() { return singleton; } } public class classloader { public static void main(String[] args) { Singleton singleton=Singleton.GetInstance(); System.out.println(singleton.count1); System.out.println(singleton.count2); } }
最后运行结果如下所示
1 1
为什么会出现上面的情况呢?如果你了解类加载器之后了,或许分析上面的代码易如反掌
类加载器的概念
类加载器(class loader)是 Java™中的一个很重要的概念。类加载器负责加载 Java 类的字节代码到 Java 虚拟机中
类加载的过程分析
上面就是一个类的加载过程图,如果看不清图,可以直接点击图片打开新页面,从图中我们发现大致有这么几步
装载:
- 通过该类型的完全限定名,产生一个代表该类型的二进制数据流。
- 解析这个二进制数据流为方法区的内部数据结构(方法区)
- 创建一个表示该类型的java.lang.Class类的实例(堆上)
连接:在类型被装载以后,就准备连接了。连接过程的第一步就是:验证 验证:字节码完验证。所有的Java需以及都必须设法为它们执行的每一个方法检查字节码的完整性。 准备:在准备阶段,Java虚拟机为类变量分配内存,设置默认初始值。但在到达初始化之前,类变量都没有被初始化为真正的初始值(准备阶段不执行Java代码)。 解析:解析过程就是在类型的常量池中寻找类、接口、字段和方法的符号引用,把这些符号引用替换成直接引用的过程。
初始化:初始化就是为类变量赋予正确的初始值。(初始化的时机-------在首次主动使用前初始化)
- 当创建某个类的新实例时(new;或者不明确的创建。反射。克隆或者反序列化)
- 调用某个类的静态方法
- 使用某个类或接口的静态字段,或者对该字段赋值(final修饰的除外,它被初始化为一个编译时的常量表达式)
- 调用Java API中的某些反射方法
- 当初始化某个类的子类时(要求超类也已经初始化)
- 当虚拟机启动时某个被表明为启动类的类(main()方法那个类)
除了上述6种情况以外,所有其他使用Java类型的方式都是被动使用。它们都不会导致Java类型的初始化。 类会在首次被“主动使用”时执行初始化,为类(静态)变量赋予正确的初始值。在Java代码中,一个正确的初始值是通过类变量初始化语句或者静态初始化块给出的。(tips:类变量就是类的静态变量)
卸载:在很多方面,Java虚拟机中类的生命周期和对象的生命周期很相似。虚拟机创建并初始化对象,使程序能使用对象,然后在对象变得不再引用后可选地执行垃圾收集。同样,虚拟机装载、连接并初始化类,使得程序能使用类,当程序不在引用它们的时候可选地卸载它们。
类加载器的类型
- 根类加载器(bootstrap class loader):它用来加载 Java 的核心库,是用原生代码来实现的,并不继承自
java.lang.ClassLoader。 - 扩展类加载器(extensions class loader):它用来加载 Java 的扩展库。Java 虚拟机的实现会提供一个扩展库目录。该类加载器在此目录里面查找并加载 Java 类。
- 系统类加载器(system class loader):它根据 Java 应用的类路径(CLASSPATH)来加载 Java 类。一般来说,Java 应用的类都是由它来完成加载的。可以通过
ClassLoader.getSystemClassLoader()来获取它。 - 用户自定义类加载器:开发人员可以通过继承
java.lang.ClassLoader类的方式实现自己的类加载器,以满足一些特殊的需求。
双亲委派模型:如果一个类加载器收到了类加载请求,它首先不会自己尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请求(在它的搜索范围中没有找到)时,子加载器才会尝试自己去加载
总结
看完以上的内容,你是不是对类加载器有一定的了解了呢,类加载器和应该算是java知识点中的难点了,从内容上来说应该上升到jvm的层次了,所以学好类加载器和了解jvm对以后的写代码也是很有帮助的
本文本来个人博客http://qihuiqiang.com/archives/283更多问题请访问http://www.qihuiqiang.com留言


浙公网安备 33010602011771号