Java的继承是比较重要的特性,也是比较容易出错的地方,下面这个例子将展现若是父类构造器中调用被子类重写的方法时会出现的状况:java
首先是父类:函数
public class test { void fun(){ System.out.println("test fun()"); } void fun1(){ System.out.println("test fun1()"); } test(){ fun(); fun1(); } public static void main(String[] args) { test t =new test(); } }
这里父类的构造器将调用一个fun方法,main函数的运行结果是:spa
test fun()
test fun1()code
而后是子类:对象
public class test2 extends test { int i = 2; test2(){ fun(); } void fun(){ System.out.println("test2 fun()"); System.out.println(i); } public static void main(String[] args) { test2 t = new test2(); } }
子类增长了一个字段i并初始化为2,并重写了fun方法,不只打印的字符串不同,还加了打印i的功能,构造器和父类同样调用了fun方法。main函数的运行结果是:blog
test2 fun()
0
test fun1()
test2 fun()
2继承
一般java的类进行初始化的时候,会先进行父类的初始化,因此会先调用父类的构造器,再进行子类的初始化,调用子类的构造器。字符串
一开始写完代码我觉得的结果是:class
test fun()
test fun1()
test2 fun()
2
我觉得就算父类的方法被重写了,也会调用本身的方法,但事实告诉咱们,父类初始化过程当中构造器若是调用了被子类重写的方法,会调用被子类重写的方法。test
还有一点,若是子类重写的方法中使用了子类才定义的字段,那这个字段的值将是该字段类型的默认值。
因此类的初始化流程总结(继承相关)就是:
1.为对象分配的存储空间初始化为二进制零。
2.调用父类的构造器,若是调用被覆盖的方法,被覆盖的方法将被调用,若是使用了子类中才定义的字段,该字段的值为该字段类型的默认值。
3.调用子类的构造器。
(这里总结的初始化流程只总结了继承相关的,正常的static部分、初始化代码仍是正常的样子)
为何会出现这种状况呢,为何须要先为对象的存储空间初始化为二进制零呢?
1.在继承中构造器的调用是分级的,先调用父类的,父类若是有父类就父类的。。。这一步是经过动态绑定实现的。
2.从概念上来讲,构造器是用来初始化对象的,可是像上面那种状况,子类重写了父类的方法,使得父类将使用子类的成员,可是此时正在初始化父类,子类尚未进行初始化。
3.基于以上两点就将为对象分配的存储空间初始化为二进制零。
因此重写父类的方法的时候须要考虑到这个特性,这种特性可能会致使父类的初始化出现问题。