• 欢迎访问天天编码网站,Java技术、技术书单、开发工具,欢迎加入天天编码
  • 如果您觉得本站非常有看点,那么赶紧使用Ctrl+D 收藏天天编码吧
  • 我们的淘宝店铺已经开张了哦,传送门:https://shop145764801.taobao.com/

Java的继承机制与构造函数(Constructor)

Java基础 tiantian 2253次浏览 1个评论 扫描二维码

Java作为一门面向对象的高级语言,自然具有面向对象语言的三大特性:封装、继承和多态。对于继承机制,子类会继承父类的成员方法和成员变量,构造函数虽然也是类的成员方法,但是却作为一种特殊的成员,被排斥在继承机制之外。构造函数不适用继承机制,而采取的另一种复用手段,使得Java中的构造函数在继承关系中变得复杂起来。本文将关注于构造函数最让人迷惑的几个方面。

Java的继承机制与构造函数(Constructor)

调用链

两个具有继承关系的类,构造子类的对象时,不仅调用了子类的构造函数,还是隐式地调用父类的构造函数,而且父类的构造函数调用顺序在前。

class Super {
String s;

public Super() {
System.out.println("Super");
}
}

public class Sub extends Super {
public Sub() {
System.out.println("Sub");
}

public static void main(String[] args) {
Sub sub = new Sub();
}
}

读者可以思考得出答案后,实际上机验证,正确的输出是:

Super
Sub

示例代码中只新建一个 Sub 类的对象,输出结果却显示,既执行了子类的构造函数,也执行了父类的构造函数,而且父类的构造函数执行先于子类构造函数。这背后的原理在于:Java中的任何一个类,其构造函数都必须首先调用其父类的构造函数。如果代码中没有明确指定调用父类的哪个构造函数(构造函数可重载),那么Java编译器在编译源码时,会自动地在子类的构造函数的最前面,插入调用父类默认构造函数(无参构造函数)的语句。这样,就保证了Java中的任何类在构造时,都先调用了其父类的构造函数。

加入编译器自动在 Sub 类中插入了对父类 Super 的调用语句,那么子类的构造函数就变成了如下所示:

public Sub() {
super();
System.out.println("Sub");
}

默认构造函数

为了更好的演示问题,我们给出一个示例代码如下:

class Super {
String s;

public Super(String s) {
this.s = s;
}
}

public class Sub extends Super {
int x = 200;
public Sub(String s) {

}

public Sub() {
System.out.println("Sub");
}

public static void main(String[] args) {
Sub sub = new Sub();
}
}

如果读者实际编译我们此处给出的示例代码,那么就会获得一个类似的错误信息:

“Implicit super constructor is undefined for default constructor. Must define an explicit constructor”

这个错误信息很明确地指出了错误原因:父类没有定义默认的构造函数。

关于Java中类的默认构造函数,我们需要知道如下四点:
1. 构造函数可以重载。
2. 默认构造函数是指没有参数的构造函数。
3. 如果类的源码中没有明确定义构造函数,编译器会自动添加默认构造函数。
4. 如果类的源码中定义了某种类型的构造函数,编译器不再添加默认构造函数。

此示例代码的父类 Super 定义了自己的构造函数,所以该类没有默认的构造函数。

既然一个类的构造函数可以有多个(重载),那么子类的构造函数在选择父类的构造函数时,是基于什么策略呢?对于子类如何选择父类构造函数的策略,答案比较简单,归结为亮点:
1. 不管是子类的有参的构造函数,还是无参的构造函数,默认都选择父类的默认构造函数。
2. 可以通过在子类的构造函数的第一行,明确指定和选择调用父类的某个具体构造函数。

此示例代码中,由于没有在子类 Sub 中指定调用要调用父类 Super 的有参构造函数。所以,子类调用的是父类的默认构造函数,而实际上,父类的默认构造函数不存在,这才导致了如上所示的错误信息。

为了解决这个编译错误,我们可以有如下三个解决方案:
1. 添加一个默认构造函数到父类 Super 的源码中。
2. 移除父类 Super 中我们自己定义的有参构造函数。
3. 在子类 Sub 的构造函数中,指定调用父类 Super 有参的构造函数。

解决方案

三种解决方案各有自己的优缺点,读者可自行体会。在此,我们给出第三种解决方法的一个示例代码,供参考。

class Super {
String s;
public Super(String s) {
this.s = s;
System.out.println("Super s");
}
}

public class Sub extends Super {
int x = 200;
public Sub(String s) {
super(s);
}

public static void main(String[] args) {
Sub sub = new Sub("www.tiantianbianma.com");
}
}

总结

通过本文的学习,我相信,读者朋友对于Java继承关系中构造函数的功能和技巧,应该可以说是完全掌握了。简单总结起来就是:子类一定会调用父类的构造函数,既可以通过源码的显式调用方式,也可以通过编译器的隐式调用方式。无论哪种方法,父类的构造函数必须被定义,既可以是源码显式定义,也可以是编译器隐式定义。
如果读者对于: 为什么提供了有参的构造函数,编译器就不提供默认的构造函数 感兴趣,欢迎加入官方的qq群:369189640,或者查看本站的这一篇文章。


天天编码 , 版权所有丨本文标题:Java的继承机制与构造函数(Constructor)
转载请保留页面地址:http://www.tiantianbianma.com/java-inherit-constructor.html/
喜欢 (1)
支付宝[多谢打赏]
分享 (0)
发表我的评论
取消评论

表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
(1)个小伙伴在吐槽
  1. 讲得很细致,清楚明白。
    匿名2017-10-16 19:15 回复 Windows 7 | Chrome 53.0.2785.104