说到继承,子类继承了父类的属性和方法,你一定会疑惑子类的生成是否会默认的生成一个父类,子类和父类又是谁先生成
这里,我们就带你揭开其中的秘密
一:无参构造器
class Super{
public Super() {
System.out.println("Super");
}
}
public class Sub extends Super{
public Sub(){
System.out.println("Sub");
}
public static void main(String[] args) {
new Sub();
}
}
>>>out:
Super
Sub这里我们看到,在Sub类中并未调用的Super构造器被调用了,
为什么会这样,因为在子类中,会默认调用父类的无参数的构造器,也就相当于调用了super()如下
public class Sub extends Super{
public Sub(){
super();
System.out.println("Sub");
}
}但当我们new Sub的时候并没有生成两个对象,为什么我们会调用父类的构造器呢?
上一章我们讲到了,子类会继承父类的属性和方法,那么当我们生成子类的时候必须要初始化父类,因此调用父类的构造器就起到了初始化父类私有属性的作用。
二:有参构造器
class Super{
private int age;
public Super(int age) {
this.age = age;
System.out.println("Super");
}
}
public class Sub extends Super{
public Sub(){
System.out.println("Sub");
}
public static void main(String[] args) {
new Sub();
}
}
这里我们试图编译的时候会提示:Implicit super constructor Super() is undefined. Must explicitly invoke another constructor
为什么呢? 因为我们默认会调用无参构造器,但是Super类中并没有这一构造器,所以我们必须手动进行调用如下:
public class Sub extends Super{
public Sub(){
super(3);
System.out.println("Sub");
}
}三:调用顺序
class Super{
public static String name;
static {
System.out.println("super static name = " + name);
name = "super";
}
public int age;
public Super(){
System.out.println("super con age = " + age);
}
public Super(int age){
System.out.println("super con arg age = " + age);
this.age = age;
}
}
public class Sub extends Super{
private static String subName;
static {
System.out.println("sub static name = " + subName);
subName = "Sub";
}
private int subAge;
public Sub(int age){
super(3);
System.out.println("sub age = " + subAge);
this.subAge = age;
}
public static void main(String[] args) {
System.out.println("main start..");
Sub sub = new Sub(5);
System.out.println("main sub age = " + sub.age);
System.out.println("main super name = " + Sub.name);
System.out.println("main sub name = " + Sub.subName);
}
}>>>out:
super static name = null
sub static name = null
main start..
super con arg age = 3
sub age = 0
main sub age = 3
main super name = super
main sub name = Sub
我们可以发现调用顺序是先从静态的开始:初始化静态属性、静态块,依照先父类后子类
然后:初始化非静态属性、块、构造器,也是依照先父类后子类的原则。
这里偷懒使用effective java中的一个例子:
class Super{
public Super(){
over();
}
public void over(){
}
}
public class Sub extends Super{
private final Date date;
Sub() {
date = new Date();
}
public void over(){
System.out.println(date);
}
public static void main(String[] args) {
Sub sub = new Sub();
sub.over();
}
}>>>out:
null
Sun Mar 25 19:38:36 CST 2018我们期望Super构造器中正确调用over方法,但根据我们的构造器调用顺序,我们发现第一次打印的是null,我们over方法被Super构造器调用的时候,sub并没有初始化date域,如果super构造器调用的over方法中有对date进行操作,那么会抛出NullPointException异常。
所以,我们在构造器中绝对不能调用可被覆盖的方法,因为超类的构造器在子类构造器之前运行,如果可被覆盖的方法依赖于子类构造器做初始化动作,那么子类尚未初始化,父类已经开始执行这可能就会导致失败。
四:域并不会被覆盖
class Super{
public String name = "Super";
}
public class Sub extends Super{
public String name = "Sub";
public static void main(String[] args) {
Super sup = new Sub();
System.out.println(sup.name);
Sub sub = new Sub();
System.out.println(sub.name);
}
}>>>out:
Super
Sub
我们发现虽然Super sup的实例是Sub 但调用sup.name的时候,我们输出的是Super,并没有把Sub类定义的name值Sub输出。
本文详细探讨了Java中子类继承父类时构造器的调用机制,包括无参构造器、有参构造器的调用及初始化顺序,并讨论了构造器中调用可覆写方法的风险。
459

被折叠的 条评论
为什么被折叠?



