JAVA基础(二)--------三大特性之--继承(中)构造器及调用顺序

本文详细探讨了Java中子类继承父类时构造器的调用机制,包括无参构造器、有参构造器的调用及初始化顺序,并讨论了构造器中调用可覆写方法的风险。

说到继承,子类继承了父类的属性和方法,你一定会疑惑子类的生成是否会默认的生成一个父类,子类和父类又是谁先生成


这里,我们就带你揭开其中的秘密

一:无参构造器

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输出。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值