Java基础-继承
前言:类的继承性是面向对象语言的基本特性,多态性的前提是继承性。Java支持继承性和多态性。本文献将探索讨论Java继承性.下一文献将续写多态性
1, Java中的继承
为了了解继承性,先看这样一个场景:一位面向对象的程序员小明,在编程过程中需要描述和处理个人信息,于是定义了类Person,如下所示:
一周以后,小明又遇到了新的需求,需要描述和处理学生信息,于是他又定义了一个新的类Student,如下所示
很多人会认为小明的做法能够理解并相信这是可行的,其实不然,问题在于Student和Person两个类的结构太接近了,后者只比前者多了一个属性school,却要重复定义其他所有的内容,实在让人“不甘心”。Java提供了解决类似问题的机制,那就是类的继承,代码如下所示:
Student类继承了Person类中的所有成员变量和方法,从上述代码可以见继承使用的关键字是extends,extends后面的Person是父类。
如果在类的声明中没有使用extends关键字指明其父类,则默认父类为Object类,java.lang.Object类Java的根类,所有Java类包括数组都直接或间接继承了Object类,在Object类中定义了一些有关面向对象机制的基本方法,如equals()、toString()和finalize()等方法。
提示:一般情况下,一个子类只能继承一个父类,这称为“单继承”,但有的情况下一个子类可以有多个不同的父类,这称为“多重继承”。在Java中,类的继承只能是单继承,而多重继承可以通过实现多个接口实现。也就是说,在Java中,一个类只能继承一个父类,但是可以实现多个接口。
面向对象分析与设计(OOAD)时,会用到UML图1,其中类图非常重要,用来描述系统静态结构。Student继承Person的类图如图1所示。类图中的各个元素说明如图2所示,类用矩形表示,一般分为上、中、下三个部分,上部分是类名,中部分是成员变量,下部分是成员方法。实线+空心箭头表示继承关系,箭头指向父类,箭头末端是子类。UML类图中还有很多关系,如图3所示,如图虚线+空心箭头表示实线关系,箭头指向接口,箭头末端是实线类。
(UML是Unified Modeling Language的缩写,即统一标准建模语言。它集成了各种优秀的建模方法学发展而来的。UML图常用的有例图、协作图、活动图、序列图、部署图、构件图、类图、状态图。)
2 调用父类构造方法
当子类实例化时,不仅需要初始化子类成员变量,也需要初始化父类成员变量,初始化父类成员变量需要调用父类构造方法,子类使用super关键字调用父类构造方法。
下面看一个示例,现有父类Person和子类Student,它们类图如下图所示所示。
父类Person代码如下:
子类Student代码如下:
在Student子类代码第①行和第②行是调用父类构造方法,代码第①行super(name, age, d)语句是调用父类的Person(String name, int age, Date d)构造方法,代码第②行super(name, age)语句是调用父类的Person(String name, int age)构造方法。
提示: super语句必须位于子类构造方法的第一行代码第③行构造方法由于没有super语句,编译器会试图调用父类默认构造方法(无参数构造方法),但是父类Person并没有默认构造方法,因此会发生编译错误。解决这个编译错误有三种办法:
- 在父类Person中添加默认构造方法,子类Student会隐式调用父类的默认构造方法。
- 在子类Studen构造方法添加super语句,显式调用父类构造方法,super语句必须是第一条语句。
- 在子类Studen构造方法添加this语句,显式调用当前对象其他构造方法,this语句必须是第一条语句
3 成员变量隐藏和方法覆盖
子类继承父类后,在子类中有可能声明了与父类一样的成员变量或方法,那么会出现什么情况呢?
1 成员变量隐藏
子类成员变量与父类一样,会屏蔽父类中的成员变量,称为“成员变量隐藏”。示例代码如下:
调用代码如下:
结果:
上述代码第①行是在ParentClass类声明x成员变量,那么在它的子类SubClass代码第②行也声明了x成员变量,它会屏蔽父类中的x成员变量。那么代码第③行的x是子类中的x成员变量。如果要调用父类中的x成员变量,则需要super关键字,见代码第④行的super.x。
2 方法的覆盖(Override)
如果子类方法完全与父类方法相同,即:相同的方法名、相同的参数列表和相同的返回值,只是方法体不同,这称为子类覆盖(Override)父类方法。
示例代码如下
调用代码如下
结果如下:
x = 20
super.x = 10
上述代码第①行是在ParentClass类声明setValue方法,那么在它的子类SubClass代码第②行覆盖父类中的setValue方法,在声明方法时添加@Override注解,@Override注解不是方法覆盖必须的,它只是锦上添花,但添加@Override注解有两个好处:
- 提高程序的可读性。
- 编译器检查@Override注解的方法在父类中是否存在,如果不存在则报错。
注意 方法覆盖时应遵循的原则: - 覆盖后的方法不能比原方法有更严格的访问控制(可以相同)。例如将代码第②行访问控制public修改private,那么会发生编译错误,因为父类原方法是protected。
- 覆盖后的方法不能比原方法产生更多的异常。
- 多态性将在下一章节阐述,期待你的关注