1、第八章继承继承张坤龙天津大学计算机学院概要概要创建子类创建子类方法覆盖方法覆盖类的层次结构类的层次结构继承与可见性继承与可见性继承的设计继承的设计继承与继承与GUITimer 类类2类A类B类C类D类F类GnJava中仅仅支持单一继承,同时Java采用Interface(接口)实现多重继承多重继承多重继承3继承继承UML 类图中,继承关系如下表示:一条带类图中,继承关系如下表示:一条带空心三角空心三角的实心箭头线的实心箭头线表示,三角指向父类表示,三角指向父类VehicleVehicleCarCar继承:是一个的关系继承:是一个的关系(is-a),即子类是父类的一个特殊版本即子类是父类的一个特
2、殊版本 Java中中,使用使用保留字保留字extends 来创建来创建继承关系继承关系class Car extends Vehicle /class contents4派生子类派生子类 Java中中,使用保留字使用保留字extends来创建继承关系来创建继承关系 参考参考 Words.java(第第298页页)参考参考 Book.java(第第299页页)参考参考 Dictionary.java(第第299页页)class Car extends Vehicle /class body5类图类图BookBook#pages:#pages:intint+pageMessagepageMessa
3、ge():void():voidDictionaryDictionary-definitions:-definitions:intint+definitionMessagedefinitionMessage():void():voidWordsWords+main(+main(argsargs:String):void:String):void表示表示protected修饰符修饰符6protected 修饰符修饰符 可见性修饰符影响父类的成员在子类中使用的可见性修饰符影响父类的成员在子类中使用的方式方式 private修饰的变量和方法不能在子类中直接引修饰的变量和方法不能在子类中直接引用用 但
4、是它们被声明为但是它们被声明为public可见性,那么他们可可见性,那么他们可以在子类中引用以在子类中引用 但是但是public 违背的封装违背的封装的原则的原则 那么还有一种可见性修饰符那么还有一种可见性修饰符:protected,它比,它比public可见性提供了更多的封装可见性提供了更多的封装,但是又没有但是又没有private封装的那么严实。封装的那么严实。7super 引用引用 由于构造函数由于构造函数不被继承不被继承,所以,所以子类构造函数子类构造函数通过通过调用调用父父类的构造函数,建立类的构造函数,建立子类子类中中父类的部分父类的部分 参考参考 Words2.java(第第30
5、1页页)参考参考 Book2.java(第第302页页)参考参考 Dictionary2.java(第第303页页)8super引用引用应由子类的构造函数负责调用父类的构造函数应由子类的构造函数负责调用父类的构造函数子类构造函数子类构造函数中中:第一行第一行应该使用应该使用super引用引用调用调用父类父类的构造函数的构造函数,否则,否则Java将自动在构造方法开始处产生将自动在构造方法开始处产生一行一行super()调用。调用。Super的第二种形式,这种用法有下面的通用形式的第二种形式,这种用法有下面的通用形式 super.member9多重继承多重继承 Java支持单继承,也就是说派生的
6、子类只能由唯一的支持单继承,也就是说派生的子类只能由唯一的父类父类 多重继承多重继承 允许子类派生自两个或者两个以上的父类,允许子类派生自两个或者两个以上的父类,子类继承所有父类的成员子类继承所有父类的成员 多重继承有时会引发一些问题,例如两个父类具有同名多重继承有时会引发一些问题,例如两个父类具有同名变量。变量。Java并不支持多重继承并不支持多重继承 多数情况下多数情况下,使用接口在某些方面来替代多重继承使用接口在某些方面来替代多重继承10概要概要创建子类创建子类方法覆盖方法覆盖类的层次结构类的层次结构继承与可见性继承与可见性继承的设计继承的设计继承与继承与GUITimer 类类11覆盖覆
7、盖、重载的比较、重载的比较 重载重载处理处理一个类一个类中的多个中的多个具有相同名称具有相同名称但是但是签名不同签名不同的的方法方法 覆盖覆盖处理的方法处理的方法,一个在一个在父类中父类中,一个在,一个在子类中子类中,两个方两个方法具有法具有相同的签名相同的签名 参考参考 Messages.java(第第305页页)参考参考 Thought.java(第第306页页)参考参考 Advice.java(第第306页页)可以用可以用final修饰符修饰符定义一个方法,子类定义一个方法,子类不能覆盖不能覆盖父类的父类的final方法方法12概要概要创建子类创建子类方法覆盖方法覆盖类的层次结构类的层次
8、结构继承与可见性继承与可见性继承的设计继承的设计继承与继承与GUITimer 类类13类的层次结构类的层次结构某个类的某个类的子类子类可以是可以是另外一个类另外一个类的的父类父类,形成类的层次结构:形成类的层次结构:动物动物马马蝙蝠蝙蝠鸟类鸟类燕子燕子哺乳动物哺乳动物具有具有相同父类相同父类的两个子类称作的两个子类称作兄弟兄弟 公共属性应该尽可能放在类层次中较高层公共属性应该尽可能放在类层次中较高层继承关系具有传递性继承关系具有传递性 因此因此,子类可以从继承子类可以从继承所有父类所有父类的成员的成员14Object 类类 Object类类在在 java标准类库的标准类库的java.lang包
9、包中定义中定义 Java中中所有的类所有的类都派生自都派生自Object类类 如果一个类定义时如果一个类定义时没有显式没有显式地地指明其父类指明其父类,那么此类继那么此类继承自承自 Object类类 因此因此,Object类类是是类层次结构类层次结构中的中的根类根类15Object类类 Object类包含包含一些被所有类继承的有用方法类包含包含一些被所有类继承的有用方法 例如,例如,Object 类中定义的类中定义的toString方法方法 每次我们定义每次我们定义toString方法,实际上是覆盖了此方法方法,实际上是覆盖了此方法从从Object类继承的定义类继承的定义 Object类中的类
10、中的toString方法返回一个字符串,这个方法返回一个字符串,这个字符串包含对象类的名称以及其它一些信息字符串包含对象类的名称以及其它一些信息16Object 类类 如果两个引用如果两个引用互为别名互为别名,Object 类中的类中的equals方法方法返回返回true 我们在定义类时,可以覆盖我们在定义类时,可以覆盖equals方法来根据实际情方法来根据实际情况确定两个对象是否相等况确定两个对象是否相等 例如,如果两个字符串对象包含相同的字符,那么例如,如果两个字符串对象包含相同的字符,那么String类定义的类定义的equals方法来返回方法来返回true 设计设计String类时,覆盖
11、了继承自类时,覆盖了继承自Object 类的类的equals方法方法17抽象类抽象类抽象类在类的层次结构中起到抽象类在类的层次结构中起到占位符的作用占位符的作用,通常用,通常用于表示一种通用概念。于表示一种通用概念。抽象类不能被实例化抽象类不能被实例化在在类的头部类的头部使用使用 abstract 修饰符来申明一个修饰符来申明一个抽象类抽象类:public abstract class Product /contents18抽象类抽象类 抽象类中通常包含没有实现的抽象方法抽象类中通常包含没有实现的抽象方法 在抽象类中在抽象类中,abstract修饰符修饰符必须必须应用于应用于每个抽象方法每个抽
12、象方法而在接口中可以省略抽象方法的而在接口中可以省略抽象方法的abstract修饰符修饰符 一个抽象类也可以包含一个抽象类也可以包含 非抽象的方法非抽象的方法 抽象类抽象类没有必要没有必要必须包含必须包含抽象方法抽象方法,即抽象类也可以没有即抽象类也可以没有抽象方法。与接口不同的是抽象方法。与接口不同的是,abstract修饰符修饰符必须必须应用应用于于每个抽象方法每个抽象方法19抽象类抽象类 抽象类的子类必须覆盖其父类的抽象方法,否则此子类抽象类的子类必须覆盖其父类的抽象方法,否则此子类依然被认为是抽象类依然被认为是抽象类 抽象方法抽象方法不能不能使用使用 final 或或 static定义
13、定义 在软件设计中,抽象类是一个重要元素:它允许我们在在软件设计中,抽象类是一个重要元素:它允许我们在层次结构中建立通用元素层次结构中建立通用元素20接口层次接口层次 与类一样,继承也可以被应用于接口,即接口可以派生与类一样,继承也可以被应用于接口,即接口可以派生出另外一个接口出另外一个接口 子接口继承了父接口中所有的抽象方法子接口继承了父接口中所有的抽象方法 实现子接口的类必须定义所有父类与子接口中的所有方实现子接口的类必须定义所有父类与子接口中的所有方法法 注意,类的层次结构与接口层次结构是不同的注意,类的层次结构与接口层次结构是不同的21概要概要创建子类创建子类方法覆盖方法覆盖类的层次结
14、构类的层次结构继承与可见性继承与可见性继承的设计继承的设计继承与继承与GUITimer 类类22重温可见性重温可见性 理解继承与可见性的细微差别是非常重要的理解继承与可见性的细微差别是非常重要的 父类所有的变量和方法,甚至是私有成员都会被子类所父类所有的变量和方法,甚至是私有成员都会被子类所继承继承 我们以前提到过,子类中不能通过名字引用私有成员我们以前提到过,子类中不能通过名字引用私有成员 但是,被子类继承的私有成员总是存在并且能被间接引但是,被子类继承的私有成员总是存在并且能被间接引用用23可见性可见性 父类可以引用自己的私有成员,因此子类可以用过调用父类可以引用自己的私有成员,因此子类可
15、以用过调用父类的方法间接引用父类私有成员父类的方法间接引用父类私有成员 即使没有父类对象存在,也可以通过即使没有父类对象存在,也可以通过Super引用指向父引用指向父类类 参考参考 FoodAnalyzer.java(第第311页页)参考参考 FoodItem.java(第第312页页)参考参考 Pizza.java(第第312页页)24概要概要创建子类创建子类方法覆盖方法覆盖类的层次结构类的层次结构继承与可见性继承与可见性继承的设计继承的设计继承与继承与GUITimer 类类25继承关系的设计继承关系的设计 正如我们前面讨论的正如我们前面讨论的,花费时间建立好的软件设计将获花费时间建立好的软
16、件设计将获得长期的效益得长期的效益 继承的设计是面向对象设计中非常重要的一部分继承的设计是面向对象设计中非常重要的一部分 恰当的设计继承关系对于软件的可维护性以及重用非常恰当的设计继承关系对于软件的可维护性以及重用非常有用有用 我们来总结一些好的软件设计在继承方面应该注意的问我们来总结一些好的软件设计在继承方面应该注意的问题题26继承设计观点继承设计观点 每一次的派生都应该具有每一次的派生都应该具有“is-a”的关系的关系 设计类层次结构有利于现有类的重用和将来潜在的软件设计类层次结构有利于现有类的重用和将来潜在的软件重用重用 在问题领域识别类和对象,找出它们的公共属性,并合在问题领域识别类和
17、对象,找出它们的公共属性,并合理地将公共属性设置在尽可能高的类层次上理地将公共属性设置在尽可能高的类层次上 通过方法的覆盖和增加修改子类的功能通过方法的覆盖和增加修改子类的功能 给子类增加新的变量时,注意避免重新定义任何继承来给子类增加新的变量时,注意避免重新定义任何继承来的变量(避免影子变量)的变量(避免影子变量)27继承设计观点继承设计观点 让每个类管理自己的数据;使用让每个类管理自己的数据;使用super引用调用父类构引用调用父类构造方法建立自己的数据造方法建立自己的数据 即使当前还不使用,也应该重新定义常用的方法,例如即使当前还不使用,也应该重新定义常用的方法,例如toString 和
18、和equals方法方法 使用抽象类代表低层类都有的通用概念使用抽象类代表低层类都有的通用概念 慎重地使用可见性修饰符,在提供子类所需的访问控制慎重地使用可见性修饰符,在提供子类所需的访问控制的同时,不要破坏封装的同时,不要破坏封装28限制继承限制继承 Final修饰符可以用于限制继承修饰符可以用于限制继承 如果如果 final 修饰符应用于一个方法,那么任何子类都修饰符应用于一个方法,那么任何子类都不能覆盖这个方法不能覆盖这个方法 如果如果 final 修饰符用于整个类,那么这个类修饰符用于整个类,那么这个类不能派生不能派生任何子类任何子类 因此抽象类不能被声明为因此抽象类不能被声明为fina
19、l 恰当地利用恰当地利用final修饰符限制继承是软件设计方案中的关修饰符限制继承是软件设计方案中的关键键29概要概要创建子类创建子类方法覆盖方法覆盖类的层次结构类的层次结构继承与可见性继承与可见性继承的设计继承的设计继承与继承与GUITimer 类类30组件类的层次结构组件类的层次结构 定义定义GUI 组件的所有组件的所有Java类都是类层次结构中的一部类都是类层次结构中的一部分分 Swing GUI 组件由组件由Jcomponent类派生,类派生,Jcomponent类由类由Container派生,派生,Container类类由由 Component 类派生类派生 许多许多Swing组件由
20、于派生自组件由于派生自Container类,因此类,因此能作能作为容器为容器 例如,例如,JLabel 对象可以包含一个对象可以包含一个ImageIcon对象对象31组件类的层次结构组件类的层次结构 Applet是体现继承的一个比较好的例子是体现继承的一个比较好的例子 回顾一下,当我们定义一个回顾一下,当我们定义一个 applet时,我们要扩展时,我们要扩展Applet 类或者类或者 JApplet 类类 Applet类和类和Japplet类已经处理了所有关于类已经处理了所有关于applet创创建、执行的所有细节,包括:建、执行的所有细节,包括:与浏览器的交互与浏览器的交互 通过通过HTML接
21、收接收applet参数参数 增强安全限制增强安全限制32组件类的层次结构组件类的层次结构 我们的我们的applet类只需要关注特定的功能类只需要关注特定的功能 当我们定义当我们定义applet的的 paintComponent方法时,我们方法时,我们实际上覆盖了原先定义在实际上覆盖了原先定义在Jcomponent类和从类和从Jppplet类继承的方法类继承的方法33事件适配器类事件适配器类 继承为我们提供了创建监听器类时,一种可供替换的技继承为我们提供了创建监听器类时,一种可供替换的技术术 我们已经知道,通过实现一个特殊的接口,我们已经知道,通过实现一个特殊的接口,(例如例如MouseList
22、ener接口接口)来创建监听器类来创建监听器类 另外我们也可以通过扩展一个事件适配器类来创建一个另外我们也可以通过扩展一个事件适配器类来创建一个监听器类监听器类 具有多个方法的每个监听器接口都有一个对应的配器类,具有多个方法的每个监听器接口都有一个对应的配器类,例如例如 MouseAdapter 类类34事件适配器类事件适配器类每个适配器类实现其对应的监听器并且提供提供空的方法定义每个适配器类实现其对应的监听器并且提供提供空的方法定义当从一个适配器类派生一个监听器类时,只需要覆盖与程序相关当从一个适配器类派生一个监听器类时,只需要覆盖与程序相关的事件方法的事件方法由于由于继承继承,不需要不需要
23、定义那些为定义那些为不使用的事件方法不使用的事件方法编写的编写的空定义空定义参考参考 OffCenter.java(第第317页页)参考参考 OffCenterPanel.java(第第317页页)35概要概要创建子类创建子类方法覆盖方法覆盖类的层次结构类的层次结构继承与可见性继承与可见性继承的设计继承的设计继承与继承与GUITimer 类类36定时器类定时器类 定时器定时器(Timer)类位于类位于javax.swing 包中,是一个包中,是一个GUI组件,但是它没有可视化界面组件,但是它没有可视化界面 定时器对象按等间隔时间产生一个动作事件定时器对象按等间隔时间产生一个动作事件 定时器对象
24、用于管理基于时间间隔的任意事件,例如:定时器对象用于管理基于时间间隔的任意事件,例如:动画动画 为了创建想象中的动作,我们使用一个定时器在适当的为了创建想象中的动作,我们使用一个定时器在适当的延时后来改变画面延时后来改变画面37定时器类定时器类 定时器类的定时器类的start和和stop方法用于开始和结束定时方法用于开始和结束定时器器 通过通过Timer 构造函数或者使用构造函数或者使用setDelay 方法来设置方法来设置延时间隔延时间隔 参考参考 Rebound.java(第第320页页)参考参考 ReboundPanel.java(第第321页页)38本章小结本章小结 从现有的类派生新类从现有的类派生新类 protected 修饰符修饰符 创建类的层次结构创建类的层次结构 抽象类抽象类 继承与可见性继承与可见性 继承的设计继承的设计 GUI组件类的层次结构组件类的层次结构 扩张监听器适配类扩张监听器适配类 Timer类类39