类加载、初始化的过程(包括static成员对象)

类加载、初始化的过程

因为之前对类加载这块不感兴趣,感觉实际应用中又用不到……但工作后发现并非如此,因此学习总结一下——类加载和初始化的过程让人很绕,有些搞不明白。

首先要明白,类的加载和初始化是两个过程,同样也是总是在一起的两个过程。(菜鸡如我一直都很糊涂)。

1、加载、初始化的含义

当我们第一次使用类时,就会触发类的加载链接初始化三个过程。(当然也有的说法会有其他过程,这里我们就简单一点,只说这三个过程)。

  • 类加载,就是查找字节码,从字节码中创建一个class对象(由类加载器执行)
  • 链接,就是验证类中的字节码,为静态域分配存储空间
  • 初始化,就是为静态成员初始化及静态代码块执行(通俗来说就是赋值)的过程

当这三个对象都完成后,我们才能进行创建对象的操作,也就是new的过程,因为创建对象必须用到类的 Class 对象。

2、类加载、初始化的时机

时机就是第一次使用类时,一般包括三种情况:

  • A的静态方法(如构造器)首次被访问时
  • A的静态成员(如 i )首次被访问时
  • 通过Class类,创建A的class对象

下面列出四种可能的情况:

public Class A extends B{
	public static Integer i = 0;
	public static Integer j = 0;
	public double k = 0;
	public double h = 0;
	static{
		print("A被初始化");
	}
	public static void main(String[] args) {
		//1、new一个对象,这是第一次使用,故A被加载并初始化
		A a = new A();
		
		//2、这是第一次使用A的静态成员,故A被加载并初始化
		print(A.i);

        //3、这次有些特殊,这时候A被加载,但并未被初始化
		Class aClass = A.class;

		//4、这时候A被加载并被初始化
		Class aClass = Class.forName("A");
	}
}

3、类加载过程

加载过程,就是创建class对象的过程。

我们都知道类都有一个class对象,假如有一个类A,通过A.class就可以获取这个对象,而实际上class对象就是用来创建对象的。

加载过程,就是创建A的Class对象,为后面创建A的对象做准备。

上面的例子我们知道获取类A的Class对象的两种方法:

  • 一种是常用的A.class
  • 一种是Class的静态方法,Class.forName(“类名,需要包含包名”)

这两种方法有一定的区别,就是A.class只会加载类,不会自动的初始化类。

        //3、这次有些特殊,这时候A被加载,但并未被初始化
		Class aClass = A.class;

		//4、这时候A被加载并被初始化
		Class aClass = Class.forName("A");

4、类初始化过程

类的初始化过程大家就比较熟悉了,是在加载之后,一般都会进行(A.class这种就没有进行)。

总结一下,初始化顺序为:

  1. 先初始化父类(如果父类未被初始化)
  2. 按顺序初始化静态成员
  3. 静态代码块执行(与上一步优先级相同,按顺序来,但是一般书写在后面)
  4. 调用new,创建对象
  5. 初始化父类的非静态成员变量,调用父类的构造函数
  6. 按顺序初始化非静态成员
  7. 非静态代码块执行(与上一步优先级相同,按顺序来,但是一般书写在后面)
  8. 执行构造器
public Class A extends B{
    public double k = 0;
	public double h = 0;
	public static Integer i = 0;
	public static Integer j = 0;
	
	//这块代码和静态变量一样,只执行一次
	static{
		i = 1;
	}
	//这块代码和非静态变量初始化时一起执行
	{
	    k = 1;
	}
	
	A(){
	    print("A构造器执行");
	}
}

如上面类A,它的初始化过程就是。

  1. 为 i 赋值0
  2. 为 j 赋值0
  3. 为 i 赋值1
  4. 为 k 赋值0
  5. 为 h 赋值0
  6. 为 k 赋值1
  7. 构造器执行,打印“A构造器执行”

5、特殊情况——静态常量

在类的初始化过程中,有一种特殊情况,就是static final修饰的常量。它不需要初始化,就可以直接访问,因为它是在编译期就准备好了。

注意,需要满足两个条件:

  • static final修饰
  • 常量

比如:

public static final Integer i = 0;

此处的 i 是不需要被初始化,就可以直接访问的。

但是下面:

public static final Integer j = ClassInitializarion.rand.nextInt(1000);

此处的 j 并不是常量,所以需要初始化之后才能访问,也就是说,访问 j 时,会触发类的初始化。

那上面的类A,用static修饰的成员,也是需要初始化吗?

public static Integer i = 0;

是的,对 i 访问时,i 需要先被链接,分配存储空间后,再执行初始化

发布了67 篇原创文章 · 获赞 32 · 访问量 6万+

猜你喜欢

转载自blog.csdn.net/weixin_43751710/article/details/104167091