构造器行成过程

  1. <init>()<clinit>()这两个构造器实际上是由Gen::normalizeDefs()收敛的过程。

  2. 编译器会把 “{}”块实例变量初始化调用父类的实例构造器等操作收敛到<init>()方法之中。 按先 执行父类的实例构造器 → 初始化变量 → 执行语句块 的顺序进行。

  3. 编译器会把 “static{}”块类变量初始化,收敛到<clinit>()方法之中。按 JVM会保证父类构造器先执行 → 初始化类变量 → 执行静态语句块 的顺序进行

读书笔记摘自 书名:深入理解Java虚拟机:JVM高级特性与最佳实践(第3版)作者:周志明

在这里插入图片描述
类构造器 (): 在类首次被加载时执行。
实例构造器 (): 在每次创建对象时执行。

在这里插入图片描述

<clinit>() 特点

  1. 它是 Javac编译器自动生成物。

  2. 编译器收集的顺序是由语句在源文件中出现的顺序决定的。 按先 初始化类变量 → 执行静态语句块 的顺序进行

  3. <clinit>()方法不需要显式地调用父类构造器,Java虚拟机会保证在子类的<clinit>()方法执行前,父类的<clinit>()方法已经执行完毕。因此在Java虚拟机中第一个被执行的<clinit>()方法的类型肯定是java.lang.Object

//由于父类的<clinit>()方法先执行,也就意味着父类中定义的静态语句块要优先于子类的变量赋值操作,如下代码清单中,字段B的值将会是2而不是1。
static class Parent {
	public static int A = 1;
	static {
		A = 2;
	}
}

static class Sub extends Parent {
	public static int B = A;
}

public static void main(String[] args) {
	System.out.println(Sub.B);
}

字段B的值将会是2而不是1
  1. <clinit>()方法对于类或接口来说并 不是必需的,如果一个类中没有静态语句块,也没有对变量的赋值操作,那么编译器可以不为这个类生成<clinit>()方法。

  2. 执行接口的<clinit>()方法不需要先执行父接口的<clinit>()方法,因为只有当父接口中定义的变量被使用时,父接口才会被初始化。此外,接口的实现类在初始化时也一样不会执行接口的<clinit>()方法。

  3. Java虚拟机必须保证一个类的<clinit>()方法在多线程环境中被正确地加锁同步,如果多个线程同时去初始化一个类,那么只会有其中一个线程去执行这个类的<clinit>()方法,其他线程都需要阻塞等待,直到活动线程执行完毕<clinit>()方法。 如果在一个类的<clinit>()方法中有耗时很长的操作,那就可能造成多个进程阻塞,在实际应用中这种阻塞往往是很隐蔽的。

  4. 静态语句块中只能访问到定义在静态语句块之前的变量,定义在它之后的变量可以赋值,但是不能访问,如下例子

public class Test {
	static {
		i = 0;                //  给变量赋值可以正常编译通过
		System.out.print(i);  // 这句编译器会提示“非法向前引用”
	}
	static int i = 1;
}

读书笔记摘自 书名:深入理解Java虚拟机:JVM高级特性与最佳实践(第3版)作者:周志明

在这里插入图片描述

实例变量初始化顺序如下

  1. 默认初始化:如果没有显式初始化,Java 会自动为成员变量提供默认值。
  2. 字段初始化:直接在字段声明时初始化成员变量。
  3. 初始化块:使用 {} 块初始化成员变量。
  4. 构造方法中的初始化:在构造方法体内初始化成员变量。

举例说明

public class Father {
    public static Integer FATHER_NUM = 1;
    static {
        System.out.println("父 → static {}, FATHER_NUM = " + FATHER_NUM);
    }

    public int age = 0;
    {
        System.out.println("父 → {}, father age : " + age);
    }

    public Father(int age) {
        this.age = age;
        System.out.println("父 → 构造方法, father age : " + age);
    }
}

public class Son extends Father {
    public static Integer SON_NUM = 1;
    static {
        System.out.println("子 → static {}, SON_NUM = " + SON_NUM);
    }

    public int age = 0;
    {
        System.out.println("子 → {}, son age : " + age);
    }

    public Son(int age) {
        super(age + 20);
        this.age = age;
        System.out.println("子 → 构造方法, son age : " + age);
    }

    public static void main(String[] args) {
        new Son(18);
    }
}

执行结果:

父 → static {}, FATHER_NUM = 1
子 → static {}, SON_NUM = 1

父 → {}, father age : 0
父 → 构造方法, father age : 38

子 → {}, son age : 0
子 → 构造方法, son age : 18
Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐