教学材料《Java概述》-第七章_第1页
教学材料《Java概述》-第七章_第2页
教学材料《Java概述》-第七章_第3页
教学材料《Java概述》-第七章_第4页
教学材料《Java概述》-第七章_第5页
已阅读5页,还剩33页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

7.1类的继承性

继承是面向对象编程技术的一块基石,因为它允许创建分等级层次的类。运用继承,能够创建一个通用类,它定义了一系列相关项目的一般特性。该类可以被更具体的类继承,每个具体的类都增加一些自己特有的东西。在Java术语中,被继承的类叫基类(baseclass)或者超类(superclass),也有很多书把它叫作“父类”;继承超类的类叫子类(subclass)或者派生类(Derivedclass)。因此,子类是基类的一个专门用途的版本,它继承了基类定义的所有实例变量和方法,并且为它自己增添了独特的元素。下一页返回7.1类的继承性

7.1.1子类对基类的继承继承一个类,只要用extends关键字把一个类的定义合并到另一个中就可以了。为了理解怎样继承,从简短的程序开始。下面的例子创建了一个基类A和一个名为B的子类。注意怎样用关键字extends来创建A的一个子类。【程序7.1】上一页下一页返回7.1类的继承性从程序7.1可以看到,子类B包括它的基类A中的所有成员。这是程序中的对象subOb可以获取i和j及调用showij()方法的原因。同样,sum()内部,i和j可以被直接引用,就像它们是B的一部分。尽管A是B的基类,它也是一个完全独立的类。作为一个子类的基类,并不意味着基类不能被自己使用,并且一个子类可以是另一个类的基类。只能给所创建的每个子类定义一个基类。Java不支持多基类的继承。这就是Java的单一继承特性。可以按照规定创建一个继承的层次。该层次中,一个子类成为另一个子类的基类。然而,没有类可以成为它自己的基类。上一页下一页返回7.1类的继承性

7.1.2成员的访问和继承尽管子类可以拥有基类的所有成员,但它不能访问基类中被声明成private的成员。例如,考虑下面简单的类层次结构:【程序7.2】该程序不会编译,因为B中sum()方法内部对j的引用是不合法的。既然j被声明成private,它只能被它自己类中的其他成员访问,子类没权访问它。根据前面所介绍的关于类成员的访问性,子类只能访问基类中由public和protected所修饰的变量和方法。上一页返回下一页7.1类的继承性

7.1.3关于继承的更实际的例子看一个更实际的例子,该例子有助于阐述继承的作用。这里,前面章节改进的Box类将被再次扩展。它包括第四成员名weight。这样,新的类将包含一个盒子的宽度、高度、深度和重量。【程序7.3】BoxWeight继承了Box的所有特征,并为自己增添了一个weight成员,这样没有必要让BoxWeight重新创建Box中的所有特征,为满足需要,只要扩展Box就可以了。下一页返回上一页7.1类的继承性

继承的一个主要优势在于一旦已经创建了一个基类,而该基类定义了适用于一组对象的属性,它可用来创建任何数量的说明更多细节的子类。每一个子类能够正好制作它自己的分类。例如,下面的类继承了Box并增加了一个颜色属性:记住,一旦已经创建了一个定义了对象一般属性的基类,该基类可以被继承以生成特殊用途的类。每一个子类能够拥有基类中定义的属性和方法,并且也能增添它自己独特的属性,这是类的继承的特质。上一页返回7.2成员隐藏和方法重写7.2.1成员的隐藏当子类定义了与基类相同名字的成员(包括成员变量与成员方法)时,基类的成员将被“隐藏”,也就是说,子类的成员的定义将覆盖基类的成员。下面这个例子展示了关于成员变量的隐藏。【程序7.4】在程序7.4中,子类B隐藏了从基类A中继承的double类型的变量y。在类Example中,“b.y=200”语句中的变量y就已经是整型了。下一页返回7.2成员隐藏和方法重写7.2.2方法的重写类层次结构中,如果子类中的一个方法与它基类中的方法有相同的方法名和类型声明,就称为子类中的方法重写(override)基类中的方法。从子类中调用重写方法时,它总是引用子类定义的方法,而基类中定义的方法将被隐藏。通过方法重写,子类可以把基类的状态和行为改变为自己的状态和行为,更重要的是,行为的外部特征(名称或类型等)不改变,这样的机制使面向对象的可替换性得以实现,也体现了面向对象的多态性。考虑下面程序:【程序7.5】上一页下一页返回7.2成员隐藏和方法重写当一个B类的对象调用show()时,调用的是在B中定义的show()版本。也就是说,B中的show()方法重写了A中声明的show()方法。如果希望访问被重写的基类的方法,可以用super关键字(将在下一节介绍)。方法覆盖仅在两个方法的名称和类型声明都相同时才发生。如果它们不同,那么两个方法就只是重写。例如,考虑下面的程序,它修改了前面的例子:【程序7.6】上一页下一页返回7.2成员隐藏和方法重写B中show()带有一个字符串参数,这使它的类型标签与A中的不同,A中的show()没有带参数,因此没有覆盖(或名称隐藏)发生。总的来说,方法在重写时一定要保证方法的名字、类型、参数个数和参数类型与基类中的方法完全相同,仅是重写子类方法的方法体内容。上一页下一页返回7.2成员隐藏和方法重写7.2.3重写与重载的区别方法的重写Override和重载Overload是Java多态性的不同表现。重写Override是基类与子类之间多态性的一种表现,重载Overload是一个类中多态性的一种表现。如果在子类中定义某方法与其基类有相同的名称和参数,则称为方法被重写(Override)。子类的对象使用这个方法时,将调用子类中的定义,对它而言,基类中的定义如同被“屏蔽”了,并且如果子类的方法名及参数类型和个数都与基类的相同,那么子类的返回值类型必须和基类的相同。如果在一个类中定义了多个同名的方法,它们或有不同的参数个数,或有不同的参数类型,则称为方法的重载(Overload)。重载的返回值类型可以相同,也可以不同。上一页返回7.3super与this

在程序7.3的例子中,从Box派生的类并没有体现出它们的实际上是多么有效和强大。例如,BoxWeight构造函数明确地初始化了Box()的width、height和depth成员,这些重复的代码在它的基类中已经存在,这样做效率很低。同时,这意味着子类必须被允许具有访问这些成员的权力。然而,有时希望创建一个基类,该基类可以保持它自己实现的细节(也就是说,它保持私有的数据成员)。这种情况下,子类没有办法直接访问或初始化它自己的这些变量。所以Java提供了该问题的解决方案,当一个子类需要引用它直接的基类时,可以用关键字super来实现。super有两种通用形式:第一种调用基类的构造函数,第二种用来访问被子类的成员隐藏的基类成员。下面分别介绍每一种用法。下一页返回7.3super与this

7.3.1使用super调用基类构造函数由于子类不能继承基类的构造方法,因此子类如果想使用基类的构造方法,必须使用关键字super,形式如下:这里,parameter-list定义了基类中构造函数所用到的所有参数。super()必须在子类构造函数中的第一个执行语句中。为了了解怎样运用super(),考虑下面BoxWeight()的改进版本:【程序7.7】上一页下一页返回7.3super与this

这里,BoxWeight()调用带w、h和d参数的super()方法。这使Box()构造函数被调用,用w、h和d来初始化width、height和depth。BoxWeight不再自己初始化这些值。它只需初始化它自己的特殊值:weight。这种方法使Box可以自由地根据需要把这些值声明成private。当一个子类调用super()时,它调用它的直接基类的构造函数。这样,super()总是引用调用类直接的基类,这甚至在多层次结构中也是成立的。还有,super()必须是子类构造函数中的第一个执行语句。上一页下一页返回7.3super与this

7.3.2使用Super访问被子类的成员隐藏的基类成员Super的第2种方法多数用于基类成员名被子类中同样的成员名隐藏的情况。在程序7.5中,如果需要调用基类中的show()方法,就可以在子类中使用super关键字,这样在子类中基类的show()方法将被调用。【程序7.8】这里,super.show()调用了基类的show()方法。super关键字不仅能在子类中调用基类的成员方法,还能访问基类中被隐藏的成员变量。上一页返回7.4创建多级类层次

目前为止,已经用到了只含有一个基类和一个子类的简单类层次结构。然而,很多时候需要建立包含任意多层继承的类层次。前面提到,用一个子类作为另一个类的基类是完全可以接受的。例如,给定三个类A、B和C。C是B的一个子类,而B又是A的一个子类。当这种类型的情形发生时,每个子类继承它的所有基类的属性。这种情况下,C继承B和A的所有方面。为了理解多级层次的用途,考虑下面的程序。该程序中,子类BoxWeight用作基类来创建一个名为Shipment的子类。Shipment继承了BoxWeight和Box的所有特征,并且增加了一个名为cost的成员,该成员记录了运送这样一个小包的费用。【程序7.9】下一页返回7.4创建多级类层次

因为继承关系,Shipment可以利用原先定义好的Box和BoxWeight类,仅为自己增加特殊用途的其他信息。这体现了继承的部分价值:它允许代码重用。该例阐述了另一个重要的知识点:super()总是引用子类最接近的基类的构造函数。Shipment中super()调用了BoxWeight的构造函数,BoxWeight中的super()调用了Box中的构造函数。在类层次结构中,如果基类构造函数需要参数,那么不论子类需不需要参数,所有子类必须向上传递这些参数。上一页返回7.5使用抽象类上一章中学习了abstract修饰符修饰的方法称为抽象方法,拥有抽象方法的类称为抽象类,本节继续进一步讨论抽象类。有些情况下,希望定义一个基类,该基类定义一种给定结构的抽象,但是不提供任何完整的方法实现。也就是说,有时希望创建一个只定义一个被它的所有子类共享的通用形式,由每个子类自己去填写细节。这样的类决定了子类所必须实现的方法的本性,这种情形下可能发生的一种情况是,基类不能创建一个方法,使其实现具有意义。当创建自己的类库时会看到,基类中的方法没有实际意义并不罕见。有两种方法可以处理这种情况:第一种,如前面的例子所示,仅仅是报告一个出错消息,这种方式在某些场合是有用的;还有一种方法就是通过子类重写该方法以使它对子类有意义。例如,考虑Triangle类,如果不定义area(),它是毫无意义的。这种情况下,希望有方法确保子类真正重载了所有必需的方法。Java对这个问题的解决是用抽象方法(abstractmethod)。下一页返回7.5使用抽象类可以通过指定abstract类型修饰符由子类重写某些方法。这些方法有时被作为子类责任(subclasserresponsibility)引用,因为它们在基类中没有指定方法的实现。这样子类必须重写它们。声明一个抽象方法,用下面的通用形式:上一页下一页返回7.5使用抽象类正如所看到的,不存在方法体。任何含有一个或多个抽象方法的类都必须声明成抽象类。声明一个抽象类,只需在类声明开始时在关键字class前使用关键字abstract。抽象类是没有对象的,也就是说,一个抽象类不能通过new操作符直接实例化,因为抽象类是定义不完全的(方法没有方法体)。并且也不能定义抽象构造函数或抽象静态方法。所有抽象类的子类都必须执行基类中的所有抽象方法或者是它自己也声明成abstract。下面是具有一个抽象方法类的简单例题。该类后面是一个执行抽象方法的类:【程序7.10】上一页下一页返回7.5使用抽象类注意程序中声明A的对象。刚刚讲过,实例化一个抽象类是不可能的。另外一点要注意:类A实现一个具体的方法callmetoo()。这是完全可接受的,抽象类可以包括它们合适的很多实现。因为Java运行时多态是通过使用基类引用实现的,所以尽管抽象类不能用来实例化,但是它们可以用来创建对象引用。这样,创建一个抽象类的引用是可行的,它可以用来指向一个子类对象。上一页返回7.6接口Java不支持多重继承,即一个类只能有一个基类,或者说一个类不可以是多个类的子类。单一继承使Java简单,易于管理程序,但是却使程序扩展不够灵活。为了克服这个不足,Java使用了接口(interface),接口解决了Java不支持多重继承的问题,可以通过实现多个接口达到与多重继承相同的功能,且比多重继承具有更强的功能。接口可以看作是没有实现的方法和常量的集合。接口与抽象类相似,接口中的方法只是做了声明,而没有定义任何具体的操作方法。接口有以下功能:①通过接口可以实现不相关类的相同行为,而不需要考虑这些类之间的层次关系。②通过接口可以指明多个类需要实现的方法。③通过接口可以了解对象的交互界面,而不需了解对象所对应的类。下一页返回7.6接口7.6.1接口的声明与使用1.接口声明我们曾使用class关键字来声明类,接口通过使用关键字interface来声明。格式如下:接口体中包含常量定义和方法定义两部分。接口体中只进行方法的声明,不允许提供方法的实现。所以,方法的定义没有方法体,且用分号“;”结尾。例如:上一页下一页返回7.6接口在接口声明中要注意:①Java系统会自动把接口中声明的变量当作static和final类型,不管是否使用了这些修饰符。并且必须赋初值,这些变量值都不能被修改。②接口中的方法默认为abstract和public,不管有没有这些修饰符。③接口若是public,该接口可被任意类实现,否则只被与接口在同一个包中类实现。④接口若为public,则接口中的变量也是public。上一页下一页返回7.6接口2.接口的使用一个类通过使用关键字implements声明自己使用一个或多个接口。如果使用多个接口,用逗号隔开接口名。类引用接口不叫继承,而称为实现。例如:这表示类A实现了接口Printable和接口Addable。这表示类Dog继承了类Animal,并实现了接口Eatable和接口Sleepable。上一页下一页返回7.6接口如果一个类要实现某个接口,那么这个类必须实现该接口的所有方法,即为这些方法提供方法体。需要注意的是,在类中实现接口的方法时,方法的名字、返回类型、参数个数及类型必须与接口中的完全一致。特别要注意的是,接口中的方法被默认是public的,所以类在实现接口方法时,一定要用public来修饰。另外,如果接口的方法的返回类型不是void的,那么在类中实现该接口方法时,方法体至少要有一个return语句。如果是void型,类体除了两个大括号外,可以没有任何语句。上一页下一页返回7.6接口Java提供的接口都在相应的包中,通过引入包可以使用Java提供的接口。也可以自己定义接口,一个Java源文件就是由类和接口组成的。看一个类实现接口的例子,在接口Computable中,声明了一个常量(值为100),两个方法:f和g;类A和类B实现了Computable。【程序7.11】从程序7.11可以看到,类A与类B都是拥有方法f和g。名称、参数、返回值类型都一样,但是方法执行的内容完全不一样,这也是面向对象多态性的体现。上一页下一页返回7.6接口7.6.2接口与多态接口的语法规则很容易记住,但真正理解接口更重要。在程序7.11中,如果去掉接口,并把a.MAX和b.MAX也去掉,程序的运行并不会有任何问题。那么为什么要用接口呢?探讨如下一个场景:轿车、卡车、拖拉机、摩托车、大客车都是汽车的子类,其中汽车是一个抽象类,如果这时汽车定义三个抽象方法:“刹车”“收取费用”“调节温度”,那么所有的子类都将要实现这三个方法,即给出方法体,产生各自的刹车、收取费用和调节温度的行为。这显然不符合人们的思维方式,因为拖拉机是没有“收取费用”和“调节温度”行为的,不能强迫拖拉机拥有它不需要的方法。上一页下一页返回7.6接口那么如果把汽车的“收取费用”“调节温度”的方法去掉呢?很明显,轿车、大客车就不能拥有这两个方法了,而这也是不符合具体情况的。那么能想到的解决方式就是允许多重继承,子类从不同的基类中继承不同的方法,但这样却又增加了子类的负担,因为并不能保证子类从若干个基类中继承的方法都是这个子类需要的。Java规定了单一继承的机制,以保证程序的健壮性和易维护,但这不利于程序的扩展,失去了灵活性。因此Java使用了接口,一个类可以实现多个接口,接口可以增加类需要的功能,不同的类可以使用相同的接口,同一个类也能实现多个接口。接口只关心功能,并不关心功能的具体实现,如“客车类”实现一个接口的“收取费用”方法,那么这个客车类必须给出怎样收取费用的操作,即给出方法的方法体。不同车类都可以实现“收取费用”,但“收取费用”的手段可能不同,这是“收取费用”的多态,即不同对象调用同一操作可能具有不同的行为。上一页下一页返回7.6接口7.6.3接口的继承关系1.接口的单继承接口可以被继承,即使用extends关键字声明一个接口是另一个接口的子接口,子接口将继承基接口中的全部方法和常量。格式如下:上一页下一页返回7.6接口看一个简单的例子:接口B继承了接口A,也就继承了方法F1(),如果一个类实现了接口B,那么它必须实现接口A和接口B中的全部方法。上一页下一页返回7.6接口2.接口的多重继承在Java中,不支持类的多重继承,但支持接口的多重继承。格式如下:要注意的是,引用接口时,必须实现接口中的所有方法,并且,创建的类如果不是抽象类,就必须实现接口的所有方法。上一页下一页返回7.6接口7.6.4一个更实际的接口例子看一个更实际的例子,来帮助理解接口。这里定义一个名为Stack的类,该类实现了一个简单的固定大小的堆栈。然而,有很多方法可以实现堆栈。例如,堆栈的大小可以固定,也可以不固定。堆栈还可以保存在数组、链表和二进制树中等。无论堆栈怎样实现,堆栈的接口保持不变。也就是说,push()和pop()方法定义了独立实现细节的堆栈的接口。因为堆栈的接口与它的实现是分离的,很容易定义堆栈接口,而不用管每个定义实现细节。让我们看下面的例子。【程序7.12】上一页下一页返回7.6接口该程序中,mystack是IntStack接口的一个引用。因此,当它引用ds时,它使用DynStack实现所定义的push()和pop()方法。当它引用fs时,它使用FixedStack定义的push()和pop()方法。已经解释过,这些决定是在运行时做出的。通过接口引用变量获得接口的多重实现是Java完成运行时多态的最有力的方法。上一

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论