Java 教案-第11章 继承和多态
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
第11章继承和多态
11.1介绍(Introduction)
面向对象编程允许从已有的类派生出新类,这叫继承(Inheritance)。继承是软件代码重用的一种机制,是一种在面向对象编程中非常重要的,而且强大的特性。假设已经定义了一个圆形,矩形和三角形的模型类,这些类有许多相同的特征,如何设计才能避免冗余,而且还能使系统易于理解和维护?答案就是使用继承。
11.2超类和子类
不同的类可能包含一些相同的,公共的特性和行为,把这些相同的东西组合在一起形成一个新的公共类来被其他类共享。继承就是定义一个一般类,然后扩展这个一般类形成更多的特殊类。这些特殊类继承了一般类的某些属性和操作。
这些类的对象我们可以称为几何对象,那么就创建一个称为几何类的一般类,这个一般类包含几何元素中的一些公共的属性和操作,如可以填充元素,修改线条的颜色,或撤销填充等。因此一般类GeometricObject可以作为所有几何对象的一般类模型。如图一般类的UML图,以及特殊类之间的关系图。
在Java术语中,一个类C1是从C2扩展来的,那么C1类称为子类,C2类称为父类或超类。子类继承父类可访问的数据和方法,同时可以扩展出自己的新的数据和方法如上图所示。代码如下。
思考一下,如下定义的构造方法是否正确?为什么?
答案是否定的,原因就是子类不能访问父类的私有的数据,但可以通过调用父类的get或set方法来访问它。
关于继承的几点注意:
1.子类不是超类的一个子集,实际上子类包含父类,并对其进行了扩展,内容比父类更丰富。
2.父类中私有的数据不能被它之外的任意类访问。因此,它们不能在子类中被直接使用。但是,如果在父类中定义了setter或getter,可以通过使用它们来访问这些数据。
3.并不是所有的is-a的关系都是继承关系,例如,一个方形是一个矩形,但却不能使用方向矩形来扩展方向,因为没有什么可扩展的。但却可以从几何类中进行扩展定义方形类。因为新的子类要比父类包含更多的详细信息。
4.继承被用来模型化is-a的关系。不要盲目的为了重用方法而扩展类。例如,没必要把一个树类扩展为人类,尽管二者具有很多相同的属性,比如高度和重量。子类和父类之间必须存在is-a的关系。
5.很多其他的编程如c++允许一个子类可以从几个父类扩展和继承,称多重继承,但Java不允许多重继承,即严格的单继承。即一个类的声明中只能有一个关键字extends,而且后面只有一个类名。如果想要拥有若干个类的特性,可以实现接口(在14章讲授)。
11.3supper关键字
子类继承了父类可访问的数据和方法,它是否继承构造方法呢?父类的构造方法可以在子类中被调用吗?在前面一章中,介绍过一个关键字this,表示对象自己。Super关键字则指它的父类,有2种方式被使用。
1.调用父类构造方法。
2.调用父类的其他方法
11.3.1 调用父类构造方法
调用父类的构造方法的格式如下:
其中super()调用父类无参数的构造方法,super(argument)调用父类和参数argument匹配的构造方法。但需注意的是,不管是使用哪条语句,调用父类的构造方法语句必须放在子类定义的构造方法之前,并且只能显示的调用父类的构造方法。如下语句是正确的。
父类的构造方法必须使用super关键字调用。构造方法被用来创建类的实例,不像属性和方法,父类的构造方法是不能继承的,只能使用super通过在子类中调用父类的构造方法。
11.3.2 构造链
构造方法可以调用重载的构造方法,或其父类的构造方法。如果二者都没显式调用,编译器会自动在子类构造方法前添加super(),如下图所示。
在任何情况下,利用构造方法创建一个类的实例时,会沿着它的继承链向上所有的父类的构造方法都要被调用。当构造一个子类对象时,子类构造方法先调用父类的构造方法,然后才能履行自己的其他任务。如果这个父类又是由另外一个类派生的,那么它父类的构造方法在调用前,先要调用它祖父的构造方法,如此,直到最终没有父亲的类的构造方法被调用,然后返回。这条构造方法的执行和返回的过程称构造链,如下代码。
如果一个类设计成被继承的类,即一般类,那么最好在类中提供无参数的构造方法来避免编程中的错误。如下代码所示。
因为在Apple类中没有显式的定义,那么Apples的默认的无参数的构造方法会被添加上。既然Apple是Fruit 的子类,那么在调用构造方法的时候,会先调用父类的构造方法,但Fruit没定义无参数的构造方法,系统不会为Fruit添加,因此这个程序不会被编译,会产生语法错误。因此如果有可能,为了避免出错,在定义任何一个类时,最好提供无参数的构造方法。
11.3.3 调用父类方法
关键字super也可以用来引用除构造方法之外的其他方法,如下语法:
如下代码:
11.4重写(Overriding)方法
子类会从父类继承方法,有时,从父类继承的方法在子类中非常有必要被修改会改进来适应新类的需要。这种对继承来方法的改写或改造称方法重写,也称方法覆盖。
例如GeometricObject类中的toString方法,返回一个代表几何对象的字串。这个方法在Circle类中可以被重
写成返回代表圆对象的字串,如下程序所示。
那么在使用的过程中是调用父类定义的方法还是子类重写的方法呢?在Circle类中,两个方法都可以被调用,使用的是具体哪个方法,根据语句的位置和具体语句的样式决定。如果像一般调用方法一样使用这个方法,则使用的是改写的方法,如果想再Circle类中使用父类定义的toString方法,则要使用super.toString()才能调用。那么能否以如下方式在Circle的子类中调用GeometricObject中定义的toString方法呢?
答案是错误的,这会发生语法错误,关于重写,有以下2点值得注意:
1.只有可访问的实例方法才能被重写,即私有方法不能被重写(因为在外类中根本不可见),如果同
一个方法在父类中被定义成了私有的方法,而在子类中又被定义了,那么这2个方法没有关系,不
是重写。
2.如实例方法一样,一个静态方法也可以被继承,但却不能被重写。如果一个在父类方法中被定义为
静态的,又被定义在子方法中,那么在父类中定义的那个静态方法会被隐藏,可以通过父类名来引
用父类的静态被隐藏的方法。
11.5重写和重载
重载意味着定义若干个名字相同,但参数和内容不同的方法。重写意味着在子类中对继承的方法进行改写。为了重写方法,方法的名字一定要和父类中定义的方法名相同,相同的参数列表和相同的返回类型。
举例来说明重载和重写的不同,如下图所示。