类加载的过程
- 加载 :从class,jar,war等加载字节码,在方法区创建CLASS对象
- 验证 : JVM规划检验,代码逻辑校验
- 准备 :为类变量【静态变量】分配空间,赋初值,为常量赋值
- 解析 :解析类的接口,方法,类,属性
- 初始化:执行静态代码块与为静态变量赋值
- 使用: 创建实例对象
- 卸载: 从JVM方法区中卸载
什么情况下不会触发类初始化
1.通过子类调用父类的静态字段,只会触发父类的初始化,而不会触发子类的初始化
2.定义对象数组,不会触发该类的初始化
3.调用类的常量,常量在编译期间会存入调用类的常量池中,本质上并未直接引用定义常量的类,不会触发定义常量所在的类
4.通过类名获取Class对象
5.通过class.forname()方法获取类对象时,指定初始化参数为false;
6.通过ClassLoader默认的loadClass方法加载类
验证1
public class ClassInit1 {
public static void main(String[] args) {
System.out.println("父亲的年龄: "+son.FatherAge);
}
}
class son extends father
{
static
{
System.out.println("子类的静态代码块执行,代表类初始化");
}
}
class father
{
static
{
System.out.println("父类的静态代码块执行,代表类初始化");
}
public static int FatherAge = 36;
}
输出结果:
父类的静态代码块执行,代表类初始化
父亲的年龄: 36
验证2
public class ClassInit1 {
public static void main(String[] args) {
father[] a = new father[1];
}
}
class father
{
static
{
System.out.println("父类的静态代码块执行,代表类初始化");
}
public static int FatherAge = 36;
}
无输出
验证3
class father
{
static
{
System.out.println("父类的静态代码块执行,代表类初始化");
}
public static int FatherAge = 36;
public final static int BIRTH_MONTH = 1;
}
public class ClassInit1 {
public static void main(String[] args) {
System.out.println("父亲的生日月份: "+ father.BIRTH_MONTH);
}
}
输出结果:
父亲的生日月份: 1
验证4:
public class ClassInit1 {
public static void main(String[] args) {
try
{
Class<?> clas = father.class;
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
无输出
验证5:
public class ClassInit1 {
public static void main(String[] args) {
try
{
Class<?> clas = Class.forName("father", false, new myloader());
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
class myloader extends ClassLoader{
}
无输出
验证6:
public class ClassInit1 {
public static void main(String[] args) {
try
{
myloader loader = new myloader();
Class<?> clas = loader.loadClass("father");
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
无输出
什么情况下会引发类的加载
1.遇到new,getstatic,putstatic,invokestatic这四条字节码指令,场景在创建实例,读写类的静态变量,调用类的静态方法时
2.使用java.lang.reflect包中方法对类进行反射调用的时候
3.初始化子类之前初始化父类
4.虚拟机启动时,用户指定执行的主类,虚拟机会先初始化这个类
待补充