类的初始化流程
加载一个类A时(执行该类的Java代码 或者 是 new 一个 该类的对象)
首先读取该类 是否存在基类B 通过extends 判断,
如果有基类B,读取其基类B,直到读取到最初始的基类C
从最初始的基类C进行初始化,再直到逐步初始化其派生类B直到加载类A
首先从类的 static 域开始加载,首先声明 static 只加载一次,将类中的 static 属性字段,方法,构造器,main函数一一加载到 static 域 中, 再次 new 一个类A的对象,static将不再加载,而是会先检查static域中是否有值。
当然 一个类加载也是分先后顺序的:
- static int i; static play();静态对象数据 (由上至下)
- Windows w1; Gird gird; 对象数据(由上至下)
- A() 构造器 构造器也是静态方法 只是没有明确使用static
/**
* 类的初始化顺序
*/
class Window {
Window(int i) {
System.out.println("Window + " + i);
}
}
class House {
// 对象数据是 由上至下加载的 w1 - > w2 - > w3
Window w1 = new Window(1);
House() {
System.out.println("House()");
w3 = new Window(33);
}
Window w2 = new Window(2);
void f() {
System.out.println("f() ");
}
Window w3 = new Window(3);
}
public class OrderOfInitialization {
public static void main(String[] args) {
House h = new House();
h.f();
}
}
结果打印
Window + 1
Window + 2
Window + 3
House()
Window + 33
f()
通常都是 static方法中(构造器也算是static修饰了-隐式)初始化字段,原则上不会调用其他方法
但是如果调用方法呢? 一般安全的做法是只能调用final方法
但是如果调用了普通方法,那么这个示例会让你明白
初始化 派生类 时候,虽然从基类开始一步步向下初始赋值,但是基类中static方法中调用的方法都会追溯到底层只使用最底层派生类的方法,
只是由于初始化顺序的原因部分字段是还未赋值,只能为系统默认初始值
abstract class BaseWithPrint {
public BaseWithPrint() {
print();
test();
}
public abstract void print();
public void test(){
System.out.println("test BaseWithPrint!");
}
}
class DerivedWithPrint extends BaseWithPrint {
int i = 47;
public DerivedWithPrint() {
print();
test();
}
public void print() {
System.out.println("i = " + i);
}
public void test(){
System.out.println("test DerivedWithPrint!");
}
}
class LastedWithPrint extends DerivedWithPrint {
int i = 100;
public void print() {
System.out.println("i = " + i);
}
public void test(){
System.out.println("test LastedWithPrint!");
}
}
public class E03_Initialization {
public static void main(String args[]) {
LastedWithPrint dp = new LastedWithPrint();
dp.print();
}
}
打印输出:
i = 0
test LastedWithPrint!
i = 0
test LastedWithPrint!
i = 100
那么把print加上final修饰会怎么样呢?
class DerivedWithPrint extends BaseWithPrint {
int i = 47;
public DerivedWithPrint() {
print();
test();
}
public final void print() {
System.out.println("i = " + i);
}
public void test(){
System.out.println("test DerivedWithPrint!");
}
}
class LastedWithPrint extends DerivedWithPrint {
int i = 100;
public void test(){
System.out.println("test LastedWithPrint!");
}
}
public class E03_Initialization {
public static void main(String args[]) {
LastedWithPrint dp = new LastedWithPrint();
dp.print();
}
}
打印输出:
i = 0
test LastedWithPrint!
i = 47
test LastedWithPrint!
i = 47
我们发现 如果追溯不到最底层的派生类,实际的过程是从基类开始追溯下层直到遇见 final 关键字结束,这一层就作为该方法的取值层了。
真是有趣的初始化呀!!