变量初始化

类初始化步骤

  1. 假如一个类还没有被加载或者连接,那就先加载和连接这个类
  2. 假如类存在直接的父类,并且这个父类还没有被初始化,那就先初始化直接的父类
  3. 假如类中存在初始化语句,那就直接按顺序执行这些初始化语句

类的初始化

在类的生命周期执行完加载和连接之后就开始了类的初始化。在类的初始化阶段,java虚拟机
执行类的初始化语句,为类的静态变量赋值,在程序中,类的初始化有两种途径:(1)在变
量的声明处赋值。(2)在静态代码处

1
2
3
4
5
6
7
8
public class Test  
{

public static int a = 0; //(1)
public static int b ;
static{
b=2; //(2)
}
}

Java程序对类的使用方式可分为两种

所有的Java虚拟机实现必须在每个类或接口被Java程序“首次主动使用”时才初始化他们

  • 被动使用
  • 主动使用
    • 创建类的实例
    • 访问某个类或接口的静态变量,或者对该静态变量赋值
    • 调用类的静态方法
    • 反射(如Class.forName(“com.bzu.csh.Test”))
    • 初始化一个类的子类
    • Java虚拟机启动时被标明为启动类的类(Java Test)

变量初始化顺序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
public class Child extends Father {


public String name = "子类";

public static int print = print();

{
System.out.println("子类代码块1");
}

public int print2 = print2();

{
System.out.println("子类代码块2");
}

public Child() {
tellName();
}

public void tellName() {
System.out.println("子类tellName: " + name);
}

public static void main(String[] args) {
new Child();
System.out.println("---");
new Child();
}

static int print() {
System.out.println("子类静态成员变量");
return 0;
}

int print2() {
System.out.println("子类实例变量");
return 0;
}


}

class Father {
public String name = "父类";
public int print4 = print4();

public static int print3 = print3();

public Father() {
tellName();

}

public void tellName() {
System.out.println("父类tellName: " + name);
}

static int print3() {
System.out.println("父类静态成员变量");
return 0;
}
int print4() {
System.out.println("父类实例变量");
return 0;
}
}

运行结果

父类静态成员变量
子类静态成员变量
父类实例变量
子类tellName: null
子类代码块1
子类实例变量
子类代码块2
子类tellName: 子类
---
父类实例变量
子类tellName: null
子类代码块1
子类实例变量
子类代码块2
子类tellName: 子类

解析

  • 初始化顺序:
    先初始化父类的静态代码—>初始化子类的静态代码–>
    (创建实例时,如果不创建实例,则后面的不执行)初始化父类的非静态代码(变量定义等)——>初始化父类构造函数——>初始化子类非静态代码(变量定义等)——>初始化子类构造函数 (ps:静态(变量、代码块)地位相同。 实例变量、构造代码块地位相同 都是按照代码的先后顺序执行 并且静态优先于非静态,静态当类第一次被初始化就执行且仅执行一次)

通过 javap -c Child 查看编译后的class
Art
Art

调用对象的实例初始化方法,在java的class文件中称之为\< init >()方法.
简单理解就是说< init >()就是从class文件字节码角度的构造函数,一般由Java代码里面的几部分构成。

超类的< init >()方法调用———————>对应super()
任意实例变量初始化方法的字节码————>对应定义变量时的赋值代码
实现了对应构造方法的方法体的字节码——>构造函数里面的代码

  • 第一次输出子类tellName: null 的原因
  1. super();调用Father类的init<>()
    1. 调用super()也就是Object类的init<>()
    2. 给父类的name赋值
    3. 执行父类的构造方法:执行构造方法是在子类中调用的,此时this是子类,所以tellName、printName方法都在子类中调用,而此时子类还没有被初始化,此时name还是null故输出name为null(ps:可以查看类的生命周期, 引用类型变量在类未初始化的默认值为null)
  2. 子类的name开始赋值
  3. 执行子类的构造方法

参考

深入java虚拟机
Java实例变量初始化