类构造器<clinit>(),实例构造器<init>()
clinit>()()和()这两个构造器的产生实际上是一种代码收敛的过程编译器会把1、(对于实例构造器而言是“{}”块,对于类构造器而言是“static{}”块2、(对于实例构造器而言是实例变量,对于类构造器而言是类变量3、仅仅是实例构造器()方法中无须调用父类的()方法,Java虚拟机会自动保证父类构造器的正确执行)等操作收敛到()和()方法之中。并且保证。
构造器行成过程
-
<init>()
和<clinit>()
这两个构造器实际上是由Gen::normalizeDefs()收敛的过程。 -
编译器会把 “{}”块,实例变量初始化,调用父类的实例构造器等操作收敛到
<init>()
方法之中。 按先 执行父类的实例构造器 → 初始化变量 → 执行语句块 的顺序进行。 -
编译器会把 “static{}”块, 类变量初始化,收敛到
<clinit>()
方法之中。按 JVM会保证父类构造器先执行 → 初始化类变量 → 执行静态语句块 的顺序进行 。
读书笔记摘自 书名:深入理解Java虚拟机:JVM高级特性与最佳实践(第3版)作者:周志明
类构造器 (): 在类首次被加载时执行。
实例构造器 (): 在每次创建对象时执行。
<clinit>()
特点
-
它是 Javac编译器 的自动生成物。
-
编译器收集的顺序是由语句在源文件中出现的顺序决定的。 按先 初始化类变量 → 执行静态语句块 的顺序进行 。
-
<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。
-
<clinit>()
方法对于类或接口来说并 不是必需的,如果一个类中没有静态语句块,也没有对变量的赋值操作,那么编译器可以不为这个类生成<clinit>()
方法。 -
执行接口的
<clinit>()
方法不需要先执行父接口的<clinit>
()方法,因为只有当父接口中定义的变量被使用时,父接口才会被初始化
。此外,接口的实现类在初始化时也一样不会执行接口的<clinit>()
方法。 -
Java虚拟机必须保证一个类的
<clinit>()
方法在多线程环境中被正确地加锁同步,如果多个线程同时去初始化一个类,那么只会有其中一个线程去执行这个类的<clinit>()
方法,其他线程都需要阻塞等待,直到活动线程执行完毕<clinit>()
方法。 如果在一个类的<clinit>()
方法中有耗时很长的操作,那就可能造成多个进程阻塞,在实际应用中这种阻塞往往是很隐蔽的。 -
静态语句块中只能访问到定义在静态语句块之前的变量,定义在它之后的变量可以赋值,但是不能访问,如下例子
public class Test {
static {
i = 0; // 给变量赋值可以正常编译通过
System.out.print(i); // 这句编译器会提示“非法向前引用”
}
static int i = 1;
}
读书笔记摘自 书名:深入理解Java虚拟机:JVM高级特性与最佳实践(第3版)作者:周志明
实例变量初始化顺序如下
- 默认初始化:如果没有显式初始化,Java 会自动为成员变量提供默认值。
- 字段初始化:直接在字段声明时初始化成员变量。
- 初始化块:使用 {} 块初始化成员变量。
- 构造方法中的初始化:在构造方法体内初始化成员变量。
举例说明
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
更多推荐
所有评论(0)