要点
- 类加载过程
- 类加载器
一、类加载过程
1.1 类的加载过程
类的加载 .class文件过程分为:加载---->连接---->初始化
三个步骤。
- 装载:查寻并加载.class文件的二进制数据读取到内存中,将器放在运行时数据区的方法区,然后在内存中,创建一个java.lang.Class对象(规范中并没有指定Class对象位于哪里,HotSpot虚拟机将其放在了方法区中),用来封装类在方法区的数据结构。(JDK8是元数据区)
加载方式有多种:本地加载;通过网络加载.class文件;zip或jar包加载.class文件等 - 链接:把类的二进制数据合并到JRE中;
a.校验:检查载入Class文件数据的正确性;
b.准备:给类的静态变量分配存储空间,并初始化默认值;
c.解析:将符号引用转成直接引用; - 初始化:对类的静态变量赋予正确的初始值,静态代码块执行初始化操作
1.2 Java类初始化
Java类的使用分为主动使用和被动使用
Java类或接口是在 首次主动使用
的时候初始化的。
- 主动使用:
1)创建类的实例(new)
2)访问类或接口的静态变量(getstatic)或对静态变量赋值(putstatic),只是该静态变量的直接类才会初始化
3)调用类的静态方法(invokestatic)
4)反射
5)初始化一个类的子类,会要求其所有父类必须先初始化
- 类初始化时,不会初始化其接口
- 子接口初始化时,不会初始化父接口
6)java虚拟机启动的时候标记为启动类的类(Junit Test 、main方法)
其他情况都是被动使用, 不会导致类的初始化。
1)引用类的static final变量(字面量,编译期可知其值)不会引起初始化
2)引用父类的static变量是不会引起子类的初始化
3)实例化类的数组,是不会导致类初始化
二、类加载器
2.1 类加载器
1)Bootstrap ClassLoader : 将存放于<JAVA_HOME>\lib目录中的,或者被-Xbootclasspath
参数所指定的路径中的,并且是虚拟机识别的类库加载到虚拟机内存中(加载jar包或class
文件)。启动类加载器无法被Java程序直接引用(用c语言编写是最顶层的加载器,在java
代码中如果尝试获取,例如:String.class.getClassLoader(),会返回null)
2)Extension ClassLoader : 将<JAVA_HOME>\lib\ext目录下的,或者被java.ext.dirs系统变
量所指定的路径中的所有类库加载;开发者可以直接使用扩展类加载器;只能加载扩展目
录下的jar包,不能加载class文件
3)Application ClassLoader : 负责加载用户类路径(ClassPath)上所指定的类库,开发者可直
接使用;(利用ClassLoader.getSystemClassLoader()可以获得);可以在家jar包或class文
件
4)最后还有一个自定义类的加载器
2.2 双亲委派机制
双亲委派机制得工作过程:
1)类加载器收到类加载的请求;
2)把这个请求委托给父加载器去完成,一直向上委托,直到启动类加载器;
3)启动器加载器检查能不能加载(使用findClass()方法),能就加载(结束);否则,抛
出异常,通知子加载器进行加载
同一个类的标准:类的全路径名必须一致;类的加载器也必须是同一个
好处:java类随着它的类加载器一起具备了一种带有优先级的层次关系。例如类java.lang.Object,它存放在rt.jar中,无论哪个类加载器要加载这个类,最终都会委派给启动类加载器进行加载,因此Object类在程序的各种类加载器环境中都是同一个类。如果用户自己写了一个名为java.lang.Object的类,并放在程序的Classpath中,该类不会被加载。
class.forName(className),反射是对className的主动使用,会导致类的初始化;而java.lang.ClassLoader.loadClass(className)并不是对类对主动使用,所以不会导致className类对初始化