文章目录
  1. java变量的初始化顺序

[TOC]

java变量的初始化顺序

假设子类B继承了父类A,当执行语句new B()或者new B(…)语句之后

1. 类的装载 (多次实例化只装载一次)

(1). 首先要进行类A和B的装载(类只有在使用New调用创建的时候才会被 java类装载器装入),先装载父类A,再装载子类B

2. 静态初始化 (多次实例化只初始化一次)

(1). 装载父类A后,完成静态动作(包括静态代码块和变量,它们的级别是相同的,按代码中出现的顺序初始化), 这里需要注意的是装载静态代码的时候,首先为静态变量分配内存空间,赋上默认值,但不把 = 后面的值赋给它(如果存在=后面的值),然后再按出现的顺序依次执行(包括static变量赋值和static代码块).

1
2
3
4
5
// 比如这种情况也是ok的
static{
i = 10;
}
public static int i = 20;

(2). 装载子类B后,同样完成静态动作,同父类A装载.

3. 实例化 (每次实例化都初始化)

(1). 在实例化子类B之前,先要实例化父类A(包括非静态代码块和变量,它们的级别是相同的,按代码中出现的顺序初始化),这里同样需要注意的是在执行代码之前,首先为成员变量分配内存空间,赋上默认值,但不把 = 后面的值赋给它(如果存在=后面的值),然后再按出现的顺序依次执行(包括实例变量赋值和实例代码块),与static初始化类似.

(2). 执行父类A的构造方法,这里如果子类B不指定,则执行父类A的默认构造方法,若父类没有默认的构造方法,则需要子类指定需要执行父类A的某个构造方法,否则编译不通过.

1
2
3
4
5
6
7
8
public B() {

}
/** 如果父类有默认构造super()就等同于如下代码 */
public B() {
super();
/** 如果父类没有默认构造super(), 则这里需要指定, 如super(...) */
}

(3). 子类B的成员实例化,与父类A实例化类似.

(4). 最后执行子类B的构造方法.

补充:
后来遇到一种情况, 存在子类方法覆盖方法的情况.

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
public class B extends A {
public int i = 10;

public B() {
System.out.println("B draw");
}
public void draw() {
System.out.println("B draw " + i);
}
public static void main(String[] args) {
B b = new B();
b.draw();
}
}
class A {
public void draw() {
System.out.println("A draw");
}
public A() {
System.out.println("begin");
draw();
System.out.println("end");
}
}

/* 输出是
begin
B draw 0
end
B draw
B draw 10
*/

这里父类的构造方法调用了draw()方法, 但实际调用的是子类中的方法, 方法中访问了变量 i, 结果为0. 所以java在实例化的时候, 都已经将类(父类和子类)中的变量和方法都已经分配好了内存空间,并且变量赋值为默认值, 然后再按照上述的规则进行初始化.

文章目录
  1. java变量的初始化顺序