《第5章继承与多态.pptx》由会员分享,可在线阅读,更多相关《第5章继承与多态.pptx(31页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、Java程序设计(第二版)程序设计(第二版)杨厚群 主编第第5章章 继承与多态继承与多态抽象性、封装性、继承性和多态性称为面向对象的四大特性。通过前面的学习,对前两个特性有了一定程度的理解,本章将继续探讨后两个特性,即继承性和多态性。本章要点类的继承;类成员的隐藏与重载;多态性;Object类和Class类;抽象类与接口;对象克隆;对象转型和类的设计原则;综合应用示例。5.1 类的继承类的继承 Java中的继承分类的继承和接口的继承两种,类的继承只支持单继承;而接口的继承可以是多重继承。本章重点讨论类的继承。在本章的前两节中,将对类的继承作详细介绍与说明。5.1 类的继承类的继承 5.1.1
2、子类的定义子类的定义子类也是类。它的定义形式与一般类的定义形式极其相似,其格式为:ModifiersclassSubClassNameextendsSuperClassName/ClassBody其中,Modifiers是修饰符,可以使用的修饰符与前一章中所介绍的一样;SubClassName是子类的名称;extends是用于实现继承的关键字,当类的定义中有extends关键字时,表示当前定义的类继承于别的类,是别的类的子类;SuperClassName是父类名;ClassBody是子类的类体。下面通过一个例子来说明类的继承机制及如何实现类的继承。5.1 类的继承类的继承 5.1.2子类的构造
3、方法Java中,在执行子类的构造方法时,必须先执行父类的构造方法,它的第一条语句必须是调用父类的构造方法的语句。调用父类的构造方法是通过语句super()来完成的。如果要调用父类的有参构造方法,则在super()的圆括号中加入所需的参数。当在子类的构造方法中通过super()来调用父类的构造方法时,如果不带参数,则这样的显式调用可以省略,它调用父类的构造方法是由系统自动进行的。但如果父类中没有显式定义无参的构造方法,则编译出错。可在父类显式声明无参构造方法,如:publicPoint()/无参构造方法当定义类时,经常都要提供一个无参的构造方法。这样,当这个类被别的类所继承时,在子类的构造方法中
4、调用父类的构造方法时才不会出错。5.2 类成员的隐藏与重载通过类的继承,子类继承了父类的成员,同时在子类中还可以添加一些新成员。但是,当在子类中添加的成员与父类中的成员同名时,系统该如何处理?5.2.1 类成员的继承类成员的继承通过继承,子类继承了父类中除构造方法以外的所有成员,这些成员称为子类的继承成员。继承成员不仅包括在超类中定义的公有(public)、受保护(protected)及私有成员(private),还包括超类的继承成员。但是在子类中,只能直接引用父类中用public或protected修饰的成员,父类中用private修饰的成员在子类中不可直接引用,因为它们受访问属性的控制。5
5、.2 类成员的隐藏与重载5.2.2 成员变量的隐藏成员变量的隐藏当在子类中添加的新成员变量与父类中的成员变量同名时,父类中对应的成员变量将被隐藏起来。在子类中,如果直接引用了这些同名的变量,则引用的是子类中定义的同名变量。当在子类中要引用父类中的同名变量时,可以通过super关键字来进行。super关键字用于指向当前类的父类。请读者结合前面所学的知识,自行区别this,super和super()三者的不同。5.2 类成员的隐藏与重载5.2.3 成员方法的重载与覆盖成员方法的重载与覆盖子类可以继承父类中的成员方法,同时也可以在子类中定义一些新的成员方法,这些新添加的成员方法有如下三种情况:(1)
6、在子类中所添加的成员方法是全新的成员方法,它与父类中的成员方法不存在同名冲突问题。这是子类对父类功能所作的一种扩展。(2)在子类中,定义了与父类同名的成员方法,但其参数不一样,这种情况称为成员方法的重载。这种重载是在子类和父类中共同完成,是子类对父类功能所作的一种扩展。(3)在子类中定义了与父类中同名同参的成员方法,这种情况被称为成员方法的覆盖。这种情况与成员变量的隐藏一样。如果在子类中或通过子类的对象直接引用同名的方法,则引用的是子类中所定义的成员方法;若要引用父类中的同名方法,需要过关键字super来完成。这是子类对父类所作的一种修改。5.2 类成员的隐藏与重载5.2.4构造方法的构造方法
7、的引用引用构造方法不能被覆盖,它们的名称总是与当前类相同。构造方法是新创建的,而不是继承而来的,子类不能直接通过方法名调用父类的一个构造方法,而是通过关键字super调用父类的一个构造方法。由于构造方法没有可供调用的方法名,要调用超类中的常规方法,常采用如下格式:super(arg1,arg2,.)注意:super语句必须是子类构造方法定义中的第一条语句。5.3 多态性5.3.1多态概念多态概念多态性表示同一种事物的多种形态;不同的对象对相同的消息可有不同的解释,这就形成多态性。java之所以引入多态的概念,原因之一是它在类的继承上只允许单继承,派生类与基类之间有“is-a”的关系。这样做虽然
8、保证了继承关系的简单、明了,但势必在功能上会有很大的限制,所以,Java引入了多态的概念来弥补这一点的不足。此外,抽象类和接口也是解决单继承规定限制的重要手段,同时,多态也是面向对象编程的精髓所在。Java中的多态性是通过方法覆盖、方法重载以及对象引用来实现的。5.3 多态性5.3.1多态概念多态概念Java中的多态性是通过方法覆盖、方法重载以及对象引用来实现的。(1)方法的覆盖实现的多态重载表现为同一个类中方法的多态性。编译时,根据方法实际参数的数据类型、个数和次序,决定究竟应该执行重载方法中的哪一个。(2)方法重载实现的多态覆盖表现为父类与子类之间方法的多态性。Java寻找执行方法的原则是
9、:从对象所属的类开始,寻找匹配的方法执行,如果当前类中没有匹配的方法,则逐层向上依次在父类或祖先类中寻找匹配方法,直到Object类。(3)对象引用的多态对象的引用型变量具有多态性,因为子类对象可以作为父类对象来使用,所以一个引用型变量可以指向不同形式的对象。5.3 多态性5.3.2多态的应用多态的应用5.4 Object类和 Class类 Java的标准系统包提供了很多类。Object类类 Object类是Java的所有类的根,如果一个类在定义时不继承于任何类,则它也有一个默认的父类,那就是Object类。Object在以下三个方面发挥了极大的作用:(1)Object类型的变量可以用来引用任
10、何类型的对象,这在多态性的实现中经常用到,可将它作为一个通用的父类,这在建立一些通用结构时极其重要。(2)将成员方法的参数设置成Object类类型的变量时,可接收任何类型的对象传来的参数,这对于很多处理是相当方便的。(3)Object提供了很多公有的(public)或保护的(protected)成员方法,这些成员方法是所有类都可能会用到的成员方法,在子类中能直接引用。方法描述publicObject()Object类惟一的一个构造方法,不带参数publicfinalClassgetClass()获得运行时的对象所属的类,返回值为Class型,关于Class在下面介绍。如多态性的处理中可用它来获
11、得真正运行的子类的类型。这是一个最终(final)方法,在子类中不能覆盖。publicinthashCode()Java的每个对象用一个称为哈希码(hash)的编码来表示,是一个整数值,它是对象的惟一标识。该方法就是用于获取对象的哈希码值的。publicbooleanequals(Objectobj)该方法将参数obj带入的一个非空对象与当前对象比较,如果它们是同一个对象,则返回true,否则,返回falseprotectedObjectclone()该方法用于实现对象的克隆,这在后续部分会作讨论。publicStringtoString()该方法常在子类中将之覆盖。它返回一个字符串,该字符串
12、描述了当前对象的一些信息,这些信息包括类名和该对象的哈希码的十六进制值,其间用分隔,它与下式等价:getClass().getName()+Integer.toHexString(hashCode()publicfinalvoidnotify()该方法用于唤醒等待当前对象的一个线程。如果等待当前对象的线程有多个,则随机唤醒其中一个。这也是一个最终(final)方法,在子类中不能覆盖。下面紧接着要介绍的4个方法也都是最终(final)方法。publicfinalvoidnotifyAll()该方法用于唤醒等待当前对象的所有线程。publicfinalvoidwait(longtimeout)该方
13、法将使当前线程等待长为timeout毫秒(milliseconds)的时间间隔,时间结束后线程自动唤醒。如果在timeout的时间内,有别的线程调用了当前对象的notify()方法或notifyAll()方法,那当前线程也被唤醒。Publicfinalvoidwait(longtimeout,intnanos)该成员方法与上面仅带一个参数的成员方法功能极其相似,它等待的时间间隔用如下公式计算:1000000*timeout+nanos。单位为纳秒(nanoseconds,即十亿分之一秒)。publicfinalvoidwait()该方法与前两个带参数的成员方法功能有所不同,它使当前线程进入等待
14、状态,只有当别的线程调用当前对象的notify()方法或notifyAll()方法时才会被唤醒。protectedvoidfinalize()这是Java中自动垃圾收集器在进行“垃圾”收集时最后调用的一个方法,它由系统自动调用。5.4 Object类和 Class类 Class类类Class类是一个公有的(public)最终类(final),它直接继承于Object类。Class类常用于描述正在运行的类或接口等。Class类没有公有的构造方法,Class的实例可通过类的对象调用getClass()方法来得到。此时,Class类的实例化其实是由Java虚拟机(JVM)来自动完成的。成员方法描述p
15、ublicStringgetName()以字符串的形式返回类名、接口名或基本数据类型名等,如:String.class.getName()返 回 java.lang.String。byte.class.getName()返回byte。publicPackagegetPackage()返回类所在的包。publicClassgetSuperclass()返回父类。publicbooleanisInterface()若当前对象是接口,则返回true,否则,返回false。publicstaticClassforName(StringclassName)该方法常用来加裁一个类。它是一个静态(stati
16、c)方法,可直接由类名Class来调用。如下面的语句用于加载java.lang包中的类Thread:Classt=Class.forName(java.lang.Thread)。5.5 抽象类与接口5.5.1抽象类,抽象类不同于一般的类,它不能实例化对象;接口是一种特殊的抽象类,用于提供规范的公共接口。Java抽象类的实现是通过关键字abstract来说明的。其格式为:ModifiesabstractclassClassName其中的成员方法可以是一般的成员方法,还可以是抽象的成员方法。抽象的成员方法通过关键字abstract来说明的。在形式上仅有方法的头部分,而没有方法体,甚至用于描述方法体
17、的一对大括号也没有,常将这样的形式称为方法的原型声明。其格式如下:ModifiesabstractreturnTypemethodName(parameterLists);abstract是声明抽象方法的关键字,returnType是方法的返回值类型,圆括号中的parameterLists是参数列表。抽象方法显然不是一个完整的方法,它也不完成任何具体的功能,只是用于提供一个接口,它只有在子类中进行覆盖后才可使用,因此,抽象方法只能出现在抽象类中。5.5 抽象类与接口关于抽象类还需作如下几点说明:(1)定义抽象类的目的是用于描述一个抽象的概念,它不能实例化对象。因此,常通过抽象类派生出子类,由子
18、类来实现具体的功能,抽象类仅提供一个统一的接口。派生出的子类可以是抽象类,也可以是非抽象的类,即一般的类。(2)如果由抽象类派生出一个非抽象的子类,则在子类中必须覆盖掉父类(抽象类)中所有的抽象方法,否则,只能将子类定义为抽象类。(3)抽象类虽然不能实例化对象,但可以定义抽象类类型的变量,由该变量可以引用此抽象类的某个非抽象子类的对象,这在多态性的实现中经常用到。(4)任何抽象方法都只能出现在抽象类中。(5)static、private和final修饰符不能用来修饰抽象类和抽象方法。5.5 抽象类与接口5.5.2 接口接口接口实质上是一种特殊的抽象类,其内部只能包含静态常量和抽象方法。接口的声
19、明接口的声明接口很象类,声明方式几乎与类相同,可以被组织成层次结构。接口的声明格式为:publicinterfaceInterfaceName/静态常量及抽象方法的声明编译器将常量的定义默认为publicstaticfinal类型的静态常量,不论是否使用了这些修饰符,它都是这样处理的。所以在定义常量时,可以只给出其数据类型说明和常量名,同时,定义时要为每个常量都赋值。因为成员方法都是抽象的,在定义成员方法时也可以省略关键字abstract,它默认也是抽象的。5.5 抽象类与接口接口的声明注意事项:(1)接口内的方法定义是公有和抽象的,如果没有包括这些限定符,它们将被自动转换为公有和抽象。(2)
20、不能在接口内将方法声明为私有(private)或保护的(protected)。(3)接口内定义的变量必须声明为公有、静态和final,或者不使用限定符。(4)声明接口时没有加上限定符public,接口不会自动将接口内的方法转换为公用和抽象的,也不会将其常量转换为公有的。(5)非公有接口的方法和常量也是非公有的,它们只能被同一个包的类或其他接口使用。5.5 抽象类与接口5.5.2 接口接口接口的继承接口的继承接口和类一样,也可以继承。不过,类仅支持单继承,而接口既支持单继承,也支持多重继承。通过继承,一个接口可以继承父接口中的所有成员。接口的继承也是通过关键字extends来说明的。多重继承是指
21、一个接口可以同时继承于两个或两个以上的接口,在子接口中,就可继承每个父接口中的常量和抽象方法。同时也可添加全新的常量或抽象方法。多重继承接口的定义形式与单继承的定义形式极其相似,只需在每个父类名之间用逗号隔开即可。5.5 抽象类与接口5.5.2 接口接口接口的继承接口的继承如果两个接口定义了相同的方法,可采取以下三种方式来解决:(1)如果两个方法的特征标相同,可以在类中实现一个方法,其定义可以满足两个接口。(2)如果方法的参数不相同,则实现方法的重载,分别实现两种方法的特征标。(3)仅当参数列表不同时,才能进行方法重载,因此如果方法的参数列表相同,而返回值不同,则无法创建一个能够满足两个接口的
22、方法。5.5 抽象类与接口5.5.2 接口接口接口实现接口实现定义抽象类也好,定义接口也好,都是为了使用。要使抽象类发挥功能,必须通过抽象类派生出一个非抽象子类,在子类中覆盖掉父类中的所有抽象方法来实现。在Java中,要让接口发挥其功能,需定义一个普通的类,在这个类中覆盖掉接口中的所有方法,以便将其完善,这称为某个类对接口的实现,实现接口是通过关键字implements来说明的。类实现接口后,其子类将继承这些新的方法(可以覆盖或重载它们),就象超类定义了这些方法一样。5.6 泛型泛型的字面意思是编写的代码适用于广泛的类型。泛型是JDK1.5中引入的一项特征,是对Java语言的类型系统的一种扩展
23、,以支持创建可以按类型进行参数化的类,是一种数据类型参数化以最大限度的进行代码重用的技术。因此在编写代码时,所适用的类型并不立即指明,而是使用参数符号来替代,具体的适用类型延迟到用户使用时才指定。5.6 泛型5.6.1 泛型声明通过使用符号“”声明泛型,里面写上泛型的名称,例如T,它仅仅是一个名称,不代表任何类型,可声明泛型类、泛型方法等,该类型T在声明这个泛型的类或方法的作用域内都可以使用。例如:class类名称如:classAT1a1;T2a2;泛型类A有T1,T2两个参数类型;在类体中,可将T1,T2当做已经存在的类型来使用。在使用泛型类时,必须用具体类型填入类型参数,即泛型类的具体化,
24、其中具体类型必须是引用型,不能为基本类型。5.6 泛型5.6.2 泛型类泛型类泛型类是指该类使用的参数类型作用于整个类,即在类的内部任何地方(不包括静态代码区域)都可以把参数类型当做一个真实的类型来使用。classPoint/这里用T来表示不确定的类型5.6.3 泛型方法泛型方法泛型方法是在方法上声明类型参数,该类型参数只可作用于声明它的方法上。/定义泛型方法publicvoidprint(Tt)System.out.println(t);5.6 泛型5.6.4 通配符泛型通配符泛型使用泛型实例时,需要为泛型指定具体的类型参数。如当使用List实例时,需要指明List中需要存放的类型List,
25、然而有时泛型实例的作用域可能更加广泛,无法指明具体的参数类型。那么使用通配符类型来指明,符号是“?”,用以代表未知类型的泛型参数。通配符类型可以应用在所有继承自Object的类上。假设X是T的任一子类型,Y是T的任一超类型,则通配符主要有两种应用形式:1)通配符表示一种模糊的泛型参数,可指代任一X,这里简称指代子类型。2)通配符表示一种模糊的泛型参数,可指代任一Y,这里简称指代超类型。另外,常将简记为5.7 对象克隆对于基本数据类型,赋值操作就是将赋值符号右侧表达式的值赋给左侧的变量。然而,当参与赋值操作的变量为引用类型(类类型)时,结果就不一样了。一个引用型变量的内容并不是所引用的那个对象的
26、具体内容,而是与该对象有关的一些存储信息。在进行对象拷贝时,希望将对象本身也拷贝过去,即生成一个新的对象,只是其中的内容和原来对象相同而已。在Java中,这样的对象拷贝是通过一种称为对象克隆的技术来实现的。实现克隆可以通过实现Cloneable接口来完成,也可以通过重写Object.clone()方法来完成。5.8对象转型和类的设计原则类的继承研究的是类之间的关系,而对象转型研究的则是具有继承关系的类的对象引用(即类类型的变量)之间的关系。对象的转型其实就是一种数据类型的转换,是类类型的变量之间的相互转换。对象转型对象转型继承是构成子类型关系的一种重要途径,类A继承类B,则类型A是类型B的子类
27、型。Java语言允许祖先类类型的引用变量引用后代类类型的对象实例。如:Bobj=newA();/超类的引用变量obj引用子类对象实例类似的,可将后代类型的引用变量隐式地转换为父类类型的引用变量,如:Aobj1=newA();Bobj2=newobj1;5.8对象转型和类的设计原则两个对象引用之间赋值遵循如下规则:(1)同个类的不同对象引用之间可以任意相互赋值;(2)子类的对象引用可以直接赋值给父类的对象引用;(3)父类的对象引用也可以赋值给子类的对象引用,但是有条件的,其条件为:只有当父类的对象引用已引用了子类的对象时,通过强制类型转换,才能将父类对象的引用赋值给子类对象的引用;如果一个父类对
28、象的引用未指向一个子类对象,那当通过强制类型转换将它赋值给一个子类对象的引用时,运行时出错。(4)没有继承关系的不同类的对象引用之间不能相互赋值。5.8对象转型和类的设计原则类的设计原则类的设计原则类设计的好坏,在很大程度上决定着整个程序的优劣。类的设计原则并无一个统一的说法,但大多对于以下几点基本认同:(1)开闭原则:一个类在扩展性方面应该是开放的,而在更改方面应该是封闭的。在设计一个类时应尽量考虑它的可扩展性,同时,一个类应该具有很好的封闭性,这主要靠数据的封装来实现。(2)替换原则:子类应当可以替换父类的使用,并出现在父类能够出现的任何地方,这主要是靠子类继承父类的所有属性和行为来完成的。(3)依赖原则:在进行业务设计时,与特定业务有关的依赖关系应该尽量依赖接口和抽象类,而不是依赖于具体类。具体类只负责相关业务的实现,修改具体类不影响与特定业务有关的依赖关系。(4)单一职责原则:就一个类而言,应该仅有一个引起它变化的原因。其带来的好处:提高内聚、降低耦合。