1. 构造器没有参数
创建爷爷类
public class GrandFather {
public GrandFather() {
System.out.println("爷爷");
}
}
创建父亲类
public class Father extends GrandFather {
public Father() {
System.out.println("爸爸");
}
}
创建A类
创建儿子类
public class A {
public A() {
System.out.println("A");
}
}
public class Son extends Father {
public Son() {
System.out.println("儿子");
}
A a = new A();
public static void main(String[] args) {
new Son();
}
}
运行结果:
爷爷
爸爸
A
儿子
结论:
- 构造过程是从父类“向外”扩散的,所以父类在子类构造器可以访问它之前,就已经完成了初始化,即使没有Son类的构造器,编译器也会默认合成一个构造器,然后调用父类的构造器。
- 虽然Son的构造方法在new A()之前,但是还是先执行了new A(),在执行Son的构造方法。也就是Son构造器初始化的时候会执行除构造函数和其他方法之外的其他代码;
接着在main方法中加入
getSuperClass(son.getClass());
getSuperClass方法体
private static void getSuperClass(Class t) {
System.out.println(t.getSuperclass());
while (t!=null){
getSuperClass(t.getSuperclass());
}
}
结果:
爷爷
爸爸
A
儿子
class javabianchengsixiang.Seven.Father
class javabianchengsixiang.Seven.GrandFather
class java.lang.Object
null(报异常)
结论:虽然没有显示的继承Object类,但是Son构造器会隐式的继承Object类
2. 构造器有参数
创建爷爷类
public class GrandFather {
public GrandFather(int i) {
System.out.println("爷爷");
}
}
注意:创建父亲类
编译器提示我们不能这样创建;
解决办法有两种:
- 在爷爷类中显示的加入构造方法
- 使用super关键字,像这样:
public class Father extends GrandFather {
public Father() {
super(666);
System.out.println("爸爸");
}
}
儿子类
public class Son extends Father {
public Son() {
System.out.println("儿子");
}
public static void main(String[] args) {
Son son = new Son();
}
}
结果:
爷爷666
爸爸
儿子
结论:如果父类中的构造器有参数,则子类必须使用super关键字来继承。
3.初始化(包含继承)全过程
准备代码
public class Father {
private int age = 50;
protected int weight;
public Father() {
System.out.println("age="+age+",weight="+weight);
weight=140;
}
private static int fa = printInit("static Facher fa initialized");
static int printInit(String s){
System.out.println(s);
return 66;
}
}
public class Son extends Father {
private int so = printInit("Son so initialized");
public Son() {
System.out.println("so="+so);
System.out.println("weight="+weight);
}
private static int son = printInit("static Son son initialized");
public static void main(String[] args) {
System.out.println("Son construct");
new Son();
}
}
结果:
static Facher fa initialized
static Son son initialized
Son construct
age=50,weight=0
Son so initialized
so=66
weight=140
过程:
- 鼠标右键点击Run Son.main()
- 类加载器开始加载Son类的编译代码(Son.class文件)。
- 在加载过程中通过extends发现有父类Father,于是Father.class被加载。(如果father有extends,则继续加载)
- 最终加载到Father.class,先执行static代码块
private static int fa = printInit("static Facher fa initialized");
打印,并且把值66赋值给变量fa - 父类static代码块执行完,会找它的子类的static代码块,也就是
private static int son = printInit("static Son son initialized");
打印,并且把值66赋值给变量son - 到现在所有的static代码块都被初始化,然后执行main方法体代码,首先执行
System.out.println("Son construct");
- 接着执行new Son();
- 首先所有对象的所有基本类型和引用类型都设为默认值
- 接着加载父类Father的基本类型和引用类型(成员变量或者非静态字段),并且赋值; private int age = 50; protected int weight;
- 接着父类Father的构造器被调用。打印,并且140赋值给变量weight;
- 接着加载子类Son的基本类型和引用类型(成员变量或者非静态字段),并且赋值
- 接着执行Son的构造函数,打印
总结参考:
第一点,所有的类都会优先加载基类
第二点,静态成员的初始化优先
第三点,成员初始化后,才会执行构造方法
第四点,静态成员的初始化与静态块的执行,发生在类加载的时候。
第四点,类对象的创建以及静态块的访问,都会触发类的加载。
下面这段话引用自:
父类的静态字段——>父类静态代码块——>子类静态字段——>子类静态代码块——>
父类成员变量(非静态字段)——>父类非静态代码块——>父类构造器——>子类成员变量——>子类非静态代码块——>子类构造器