java 类加载及实例化的调用顺序

1.没有继承的情况

单独一个类的场景下,初始化顺序为依次为 静态变量和静态代码块(看两者的书写顺序),继承的基类的构造函数,成员变量,被调用的构造函数。

代码呈现:

public class Test {
	public static void main(String[] args) {
		Son son = new Son();
	}
}

class Son {
	public Son() {
		System.out.println("this is son.");
	}

	public Son(int age) {
		System.out.println("son is " + age + " years old.");
	}

	private Height height = new Height(1.8f);

	static {
		System.out.println("this is static code");
	}
	
	public static Gender gender = new Gender(true);
}

class Height {
	public Height(float height) {
		System.out.println("initializing height " + height + " meters.");
	}
}

class Gender {
	public Gender(boolean isMale) {
		if (isMale) {
			System.out.println("this is a male.");
		} else {
			System.out.println("this is a female.");
		}
	}
}
this is static code
this is a male.
initializing height 1.8 meters.
this is son.

2.继承的情况

稍微修改一下代码,添加两个基类,让Son继承Father, Father继承Grandpa。
继承的情况就比较复杂了。由于继承了基类,还将往上回溯,递归地调用基类的无参构造方法。
在我们的例子中,在初始化静态数据后,会先往上追溯,调用Father的默认构造方法,此时再往上追溯到Grandpa的默认构造方法。

注:如果在子类的构造方法中,显式地调用了父类的带参构造方法,那么JVM将调用指定的构造方法而非默认构造方法。

基类和子类均有静态数据,成员变量和构造方法的场景

我们继续修改代码,让其最终呈现如下:

public class Test {
	public static void main(String[] args) {
		Son son = new Son();
	}
}

class Grandpa {
    public Grandpa() {
        System.out.println("this is grandpa.");
    }

    public Grandpa(int age) {
        System.out.println("grandpa is " + age + " years old.");
    }

    private Height height = new Height(1.5f);

    public static Gender gender = new Gender(true, "grandpa");
    
    static {
		System.out.println("this is static code");
	}
}

class Father extends Grandpa {

    public Father() {
        System.out.println("this is father.");
    }

    public Father(int age) {
        System.out.println("father is " + age + " years old.");
    }

    private Height height = new Height(1.6f);

    public static Gender gender = new Gender(true, "father");
}

class Son extends Father {

    public Son() {
        super(50);
        System.out.println("this is son.");
    }

    public Son(int age) {
        System.out.println("son is " + age + " years old.");
    }

    private Height height = new Height(1.8f);

    public static Gender gender = new Gender(true, "son");
}

class Height {
    public Height(float height) {
        System.out.println("initializing height " + height + " meters.");
    }
}

class Gender {
    public Gender(boolean isMale) {
        if (isMale) {
            System.out.println("this is a male.");
        } else {
            System.out.println("this is a female.");
        }
    }

    public Gender(boolean isMale, String identify) {
        if (isMale) {
            System.out.println(identify + " is a male.");
        } else {
            System.out.println(identify + " is a female.");
        }
    }
}
grandpa is a male.
this is static code
father is a male.
son is a male.
initializing height 1.5 meters.
this is grandpa.
initializing height 1.6 meters.
father is 50 years old.
initializing height 1.8 meters.
this is son.

在我们的示例中,加载顺序应该是这样的:

Grandpa 静态数据
Father 静态数据
Son 静态数据
Grandpa 成员变量
Grandpa 构造方法
Father 成员变量
Father 构造方法
Son 成员变量
Son 构造方法

一般来说,顺序如下:

1.首先是父类的静态变量和静态代码块(看两者的书写顺序);
2.第二执行子类的静态变量和静态代码块(看两者的书写顺序);
3.第三执行父类的成员变量赋值
4.第四执行父类类的构造代码块
5.第五执行父类的构造方法
6.执行子类的构造代码块
7.第七执行子类的构造方法

总结,也就是说虽然客户端代码是new 的构造方法,但是构造方法确实是在整个实例创建中的最后一个调用
先静态:具体是先父静态>子静态。
先父后子:先父的全部,然后后子的全部。
优先级:父类>子类,静态代码块>非静态代码块>构造函数(与位置前后无关系)

posted on 2018-12-08 11:08  疯狂的小萝卜头  阅读(352)  评论(0编辑  收藏  举报