一、类加载的顺序
1、加载:将字节码加载到方法区,先加载父类再加载子类。
2、连接
(1)验证:保证字节码文件的正确性
(2)准备:为类变量(非引用类型)分配空间和赋初始值0,准备阶段先赋0,初始化会被赋1
注意:a.finall修饰的的static属性(非引用类型)的默认值是编译期就设置好了的。
b.这个阶段不会为实例对象分配空间和赋初始值,因为类变量是分配在方法区中,而实例对象是随着java对象分配在jvm堆中的。
c.第(2)中都是“非引用类型”,是因为引用类型属性的分配空间和赋值都是在初始化中完成的。
(3)解析:将常量池中的符号引用解析为直接引用。
比如:类C定义了一个属性:Object o = new Object();
如果未解析:则常量池中只有 "o"这个符号存在;
如果解析完成:则会讲"o"这个符号替换成实际的地址值,比如:x00987。
3、初始化:初始化是指调用方法,初始是惰性的
会触发初始化的操作:
(1)使用new关键字创建对象;
(2)首次访问一个类的静态变量或静态方法时;
(3)子类初始化时,若父类未初始化,则会触发父类的初始化;
(4)子类访问父类的静态变量,只会引起父类的初始化;
(5)Class.forName(初始化指定的类);
(6)main方法所在的类会优先初始化;
不会触发初始的操作:
(1)触发finall类型的(非引用类型)的属性,不会触发初始化;
(2)loadClass方法只会加载字节码,不会触发初始化;
(3)Class.forName设置初始化参数为false时,不会触发初始化;
(4)类对象.class,不会触发初始化;
(5)创建该对象的数组时,不会触发初始化;
二、类的生命周期
1、加载
2、验证
3、连接(验证,准备,解析)
4、初始化
5、引用
(1)主动引用
a. 通过new关键字实例化对象
b. 通过反射实例化对象
c. 初始化子类的时候会初始化父类
d. 调用类的main方法时
(2)被动引用
a. 引用父类的静态字段,只会引起父类的初始化,而不会引起子类的初始化
b. 定义类的数组,不会引起类的初始化
c. 引用类的常量,不会引起的类的初始化
总结:引用分为主动引用和被动引用,主动引用会引起类的初始化,被动引用不会引起类的初始化。
6、卸载
(1)该类的所有实例都已经被回收,也就是java堆中不存在该类的任何实例
(2)加载该类的ClassLoader已经被回收
三、双亲委派机制
1、了解常用的类加载器
类加载器 | 加载的路径 | 说明 | 父类 |
---|---|---|---|
Bootstrap ClassLoader(启动类加载器) | JAVA_HOME/jre/lib/ | 用来加载核心库,比如包名为java,javax,sun开头的类,如java.lang.String | 无 |
Extension ClassLoader(扩展类加载器) | JAVA_HOME/jre/lib/ext | Bootstrap ClassLoader | |
Application ClassLoader(系统类类加载器) | 加载classpath下的类 | Extension ClassLoader |
2、双亲委派机制
java虚拟机加载字节码文件时是采用按需加载,也就是用到该类时才将它的字节码文件加载到内存中生成Class对象。如果一个类加载器收到了加载请求,他自己是不会先去加载的,而是先委托给父类加载,如果父类不负责该文件加载则父类会委托回给子类加载。
3、双亲委派机制的好处与作用
(1)保证类不会重复加载
(2)保证了沙箱安全机制。比如我们自定义来java.lang包,并新建了一个名为Demo的类,此时加载这个类会通过双亲委派机制,委派给启动类加载器加载,这时加载就会报错,因为是不允许在核心包下自定义新的类的。这样就保护来java核心包下的代码的安全性。
4、如何判断两个类是否相同
(1)包名和类名必须完全相同
(2)使用的类加载器必须是同一个,类加载器不同,加载的实例就会不同