1、继承和多态,1.类的继承,在程序设计中,很多设计出来的类存在包含关系,一个类的内部会包含和其它类类似的特征和属性,在设计时可以以另外一个类为基础进行设计,这个特性就是面向对象设计中的继承性。在一个项目中,如果类和类之间存储包含关系,即一个类是另外一个类的一种,就可以使用继承。继承性提供了全新的类设计方式,可以充分利用已有类内部的结构和功能,极大的降低类内部的代码重复,是设计类的一种显著的变革,对于大型的项目设计十分有用。另外很多技术的应用中也包含大量的继承成分,使整个技术体系比较固定。,继承的概念,继承,就是在已经存在的类的基础上,再进行扩展,从而产生新的类。已经存在的类称为父类、超类或基类,
2、而新产生的类称为子类或派生类。Java 中类的继承关系如图,继承特征,几个实例,人,中学生,经理,普通员工,考察以下例子,货车:属性:车牌号、车类型、购车价格、出租单价、出租里程数、吨位数、租金。方法:租金计算等。大客车: 属性:车牌号、车类型、购车价格、出租单价、出租里程数、出租小时数、坐位数、租金。方法:租金计算等。小轿车: 属性:车牌号、车类型、购车价格、出租单价、出租里程数、出租天数、租金。方法:租金计算等。,各类车之间的关系图示,父类(或基类、超类),子类(或派生类),2. 继承的实现,(1)定义类继承是通过 extends 关键字实现的。格式表示如下:类的修饰符 class ext
3、ends ;,继承的语法,编程例51继承的语法使用关键字extends格式:public class SubName extends SupName如:public class Manager extends Empoyee ,成员属性与成员方法的继承规则,扩展子类、编程实践,扩展子类为子类添加新的成员属性;为子类添加新的成员方法;调试例51,实战演习,1.创建一个具有确定位置矩形类PlainRect,该类继承于第三章中例子中的类Rect2,其确定位置用矩形的左上角坐标来标识,为该类添加:两个属性:矩形左上角坐标startX和startY。一个方法:方法isInside(double x,do
4、uble y)。用于判断某个点是否在矩形内部,如在矩形内,返回true, 否则,返回false。2编写上题PlainRect类的测试程序(1)创建一个左上角坐标为(10,10),长为20,宽为10的矩形对象;(2)计算并打印输出矩形的面积和周长;(3)判断点(25.5,13)是否在矩形内,并打印输出相关信息。,分析与讨论,衬衣与裙子类具有许多相同的属性,能否将它们看成一种继承关系?,深入理解继承概念,面向对象中的继承是一个基于分类学的概念,继承描述的是一种“is a”的关系。子类是父类在同一个分类分支上的一个更为特殊的类,只有当类之间存在的是一种能够被描述为:“子类 is a 父类”的时候,我
5、们才使用继承。因此,在面向对象程序设计中,Skirt与Shirt类不能表示为一种继承关系。,示例代码,/Animal.javapublic class Animal String name; /类型名称int moveType; /移动方式/Mammalia.javapublic class Mammalia extends Animalint fosterTime; /哺育时间这里Mammalia类就是Animal类的子类,Animal类就是Mammalia类的父类,子类和父类具有相对性,正如一个祖孙三代的家庭内部,每个人相对于不同的人扮演不同的角色一样。同时类和类之间的继承具备传递性,就如
6、现实中的血缘关系一样。,继承说明,两个类之间如果存在了继承关系以后,将带来哪些不同呢?下面依次来进行说明:1.子类拥有父类的所有属性 子类中继承父类中所有的属性,在父类中声明的属性在子类内部可以直接调用。说明:如果访问控制符限制则无法访问。2. 子类拥有父类的所有方法 子类中继承父类中所有的方法,在父类中声明的方法在子类内部可以直接调用。说明:如果访问控制符限制则无法访问。3.子类不拥有父类的构造方法 子类不继承父类的构造方法,如果需要在子类内部使用和父类传入参数一样的构造方法,则需要在子类内部重新声明这些构造方法。4. 子类类型是父类类型 子类类型的对象可以自动转换为父类类型的对象,父类类型
7、的对象则需要强制转换为子类的对象,转换的语法个基本数据类型转换的语法相同。5.Java语言不支持多继承,一个类只能直接继承一个类,子类构造器,为Vehicle类添加构造器方法,观察编译结果子类构造器构造器主要用来完成对类成员属性进行初始化,子类中的属性包含从父类继承下来的属性和自己新添加的属性属性初始化的顺序规定为:先初始化从父类中继承的属性,再初始化子类新添加属性。格式: constructor() super(父类构造器参数); /调用父类构造器 其它; /对本类属性进行初始化或其它处理代码,属性隐藏,在子类中可以再定义一个与父类中已定义的属性名完全相同的属性,这种情况下这个子类就拥有了两
8、个名字相同的变量,一个继承于父类,另一个是自己定义的。属性隐藏如图所示。,隐藏属性使用,使用子类对象时,子类对象引用默认的是子类中定义的属性,而父类的属性隐藏。要操作父类中的同名属性时,可使用 super 关键字引导。Super 表示的是当前对象的直接父类对象。例:class B extends A private int x; private int y; public B() x = super.x + 200;y = super.y + 200,子类对父类属性的隐藏,如果子类声明了一个与父类的成员变量同名的成员变量,则称父类的该成员变量被隐藏(hiding)下面代码段的输出结果是?,cl
9、ass A protected int x=10;public class B extends A private int x=20; void print() System.out.println(x=+x); public static void main(String args) B b=new B(); b.print(); ,1.输出x=20还是x=10?2.如何访问从父类继承下来的x值?,this与super,每个类中有两个隐含的对象this与super:this是代表本类的一个对象,可以通过this.属性的形式来引用本类的属性;super是代表父类的一个对象,可以通过super.
10、属性的形式来引用父类的属性;使用this与super可以解决父类属性被隐藏的情况;如果在方法内部也有与类成员属性同名的变量,可以这样来区分它们(以变量名为x为例):x:代表方法的局部变量this.x:代表本类的成员属性值super.x:代表从父类继承下来的但被隐藏的x值,方法覆盖,子类可以重新定义从父类继承来的方法,称之为方法覆盖。静态方法只能能被继承,不能被覆盖。如果在子类中重新定义了父类中的静态方法,则在子类中隐藏了父类的静态方法。实现方法覆盖一定是在子类重写与父类方法名、返回值类型和参数列表完全相同方法。如果有一项不同则是在子类中添加了一个新的方法。,子类对父类方法的覆盖,如果子类声明了
11、一个与父类的成员方法同名的成员方法,则称父类的该成员方法被重写或覆盖(overriding)方法覆盖是Java实现多态性机制的另一种方式。出现方法覆盖时,同样可通过this和super来区分是父类方法还是子类方法。方法覆盖规则:1.方法名、参数个数、对应参数类型一样,也称为方法签名一样;2.返回值类型一样;3.子类方法的访问控制较父类方法访问控制修饰更松;,方法覆盖举例,圆形(Circle)是一种图形,三角形(Triangle)也是一种图形,图形(Shape)是可以画出来的,但是圆形和三角形的画法是不同的,它们的画法(draw()实现及继承关系如图,方法覆盖,/Monster.javapubl
12、ic class Monsterpublic void move() /移动功能 /Boss.javapublic class Boss extends Monsterpublic void move()/Boss类的移动规则/NormalMonster.javapublic class NormalMonster extends Monsterpublic void move()/ NormalMonster类的移动规则,这样在Monster的每个子类内部都重新书写了move方法的功能,这种在子类内部重新父类中的方法的语法现象,称作方法覆盖(override)。方法覆盖在实际中保持了类的结构
13、的统一,在实际使用时将极大的方便程序开发人员的使用,使项目的整体结构保持统一,便于项目的维护。在使用子类的对象时,子类内部的方法将覆盖从父类继承过来的方法,也就是说子类的对象调用的是子类的功能方法,而不是父类的方法。在进行方法覆盖时,子类内部的方法和父类的方法声明相同,而且子类方法的限制不能比父类的方法严格。例如不能使用比父类限制更大的访问控制符或抛出比父类更多的异常等,这个在实际使用方法覆盖时需要特别的注意。在实际的项目中大量的存在需要在子类内部重写父类的功能方法的地方,恰当的使用方法覆盖将为项目开发带来很大的便利。,需要注意的问题,1、 属性覆盖没有必要 方法覆盖可以重写对应的功能,在实际
14、继承时在语法上也支持属性覆盖(在子类内部声明和父类属性名相同的属性),但是在实际使用时修改属性的类型将导致类结构的混乱,所以在继承时不能使用属性覆盖。2、 子类构造方法的书写 该项是继承时书写子类最需要注意的问题。在子类的构造方法内部必须调用父类的构造方法,为了方便程序员进行开发,如果在子类内部不书写调用父类构造方法的代码时,则子类构造方法将自动调用父类的默认构造方法。而如果父类不存在默认构造方法时,则必须在子类内部使用super关键字手动调用。说明:子类构造方法的参数列表和父类构造方法的参数列表不必完全相同。,需要注意的问题,3、 子类的构造过程 在构造子类时由于需要父类的构造方法,所以实际
15、构造子类的过程就显得比较复杂了。其实在实际执行时,子类的构造过程遵循:首先构造父类的结构,其次构造子类的结构,无论构造父类还是子类的结构,都是首先初始化属性,其次执行构造方法。则子类的构造过程具体如下:如果类A是类B的父类,则类B的对象构造的顺序如下: a) 类A的属性初始化 b) 类A的构造方法 c) 类B的属性 d) 类B的构造方法由于任何一个类都直接或间接继承自Object类,所以Object类的属性和构造方法都是首先执行的。4、 不要滥用继承 在实际的项目设计中,继承虽然很经常使用,但是还是不能滥用,使用继承的场合以及相关问题参看下面的说明。,方法成员的覆盖和成员变量的隐藏的不同之处在
16、于:子类隐藏父类的成员变量只是使它不可见,父类的同名成员变量在子类对象中仍然占有自己的存储空间;而子类成员方法对父类的同名方法的覆盖将清除父类方法占有的内存空间,使得父类的方法在子类对象中不复存在,而完全由子类方法所取代。方法覆盖与方法重载的区别:方法重载要求方法名相同,参数个数或参数类型不同,而方法覆盖要求子类的方法应保持与父类完全相同的方法。,类和类之间的关系主要有三种,1、 没有关系 项目中的两个类之间没有关联,不需要进行消息传递,则这两个类之间就没有关系,可以互相进行独立的设计。2、 使用关系(has-a) 如果一个类的对象是另外一个类的属性,则这两个类之间的关系是使用关系。例如把房屋
17、(House)看作是一个类,把门(Door)看成另外一个类,则房屋有一个门。 public class Housepublic Door door; public class Door 则这里Door的对象是House类的属性,则Door和House类之间的关系就是使用关系,House使用Door类来制作自身。使用关系提供了使用已有类来声明新类的方式,可以以组合的方式来构建更复杂的类,这是项目中使用类的常见方式之一。判断是否是使用关系的依据就是:has-a,一个类具备另外一个类的对象,例如一个House有一个门。3、 继承关系(is-a) 如果一个类是另外一个类的一种,也就是在分类上存在包含关
18、系,则应该使用继承来实现。例如Boss是怪物的一种,则使Boss继承Monster类。,如何设计继承,在实际设计继承时,一般有两种设计的方法:1、 自上而下的设计 在实际设计时,考虑类的体系结构,先设计父类,然后根据需要来增加子类,并在子类的内部实现或添加对应的方法。2、 自下而上的设计 在实际设计时,首先不考虑类的关系,每个类都分开设计,然后从相关的类中把重复的属性和方法抽象出来形成父类。对于初学者来说,第二种设计方式相对来说比较容易实现,所以一般初学者都按照第二种设计方式进行设计,设计完成以后再实现成具体的代码。,Java的单继承结构和Object类,继承具有传递性子类继承沿继承路径向上的
19、所有父类的有关属性和方法。Java的继承结构Java中的每个类最多只能有一个父类。当在类的定义时没有使用extends从任何类中继承时,默认继承与Object类。,对象造型,上溯造型:自动进行Vehicle vehicle=new Truck();下溯造型:Vehicle employee=new Truck();Truck truck=(Truck)Vehicle;,多态性,多态性定义允许在同一个类或者具有继承关系的父类与子类中定义名称相同但功能不同的方法,引用时使用同样的方法名所完成的功能并不一定相同。多态性有两种形式编译多态性、运行多态性。,编译多态性,在同一个类中,可以定义多个名称相同
20、的方法,但这些名称相同的方法在参数、返回值类型却不尽相同,引用时根据参数或返回值类型不同自动执行相关的操作,编译多态性是通过方法重载实现的,Java 虚拟机会根据传递给方法的参数类型以及个数来判断到底决定调用哪个重载方法。,编译多态性,Point 类实现代码如下:class Point private float x, y;public Point() x = 0;y = 0;public Point(float f) x = f;y = f;public Point(float f1, float f2) x = f1;y = f2;,编译多态性,例子分析构造函数 Point 有三种形式,这
21、三种形式使用同一个名称,但参数不同,使用时分别为:Point A = new Point();或Point A = new Point(6.5);或Point A = new Point(7.8, -12);系统会自动根据所带参数不同调用与其形式相匹配的方法。,运行多态性,运行多态性是在子类中定义了与父类中完全相同的方法,引用时是根据对象类型不同从而决定到底是执行父类中的方法还是执行子类中的方法。运行多态性是通过方法的覆盖实现,方法在父类中定义,在子类中覆盖,对象可以通过引用子类的实例来调用子类的方法,子类中调用哪个方法是在运行时决定的,取决于对象的类型。,运行时多态性,概念运行时多态性:同一
22、种方法的调用形式,在运行过程中表现出多种形态的特点。实现运行时多态技术的条件:有一个继承(或实现接口的)层次关系;在子类中重写父类的(实现接口的)方法;通过父类的引用对子类对象进行调用;,运行时多态性,例子 A 类与 B 类定义如图,A 类的实例对象引用 B 类的方法 test()class A void test() System.out.println(class A);class B extends A void test() System.out.println(class B);public class TestAB public static void main(String ar
23、gs) A a = new A();B b = new B();A c = new B();a.test();b.test();c.test();,程序结果分析:程序结果为:class Aclass Bclass B分析对实例 a 和 b,它们的类型确定,分别为 A 和 B,所以直接调用相应的方法test()即可。对实例 c,虽然定义类型为 A,但它是子类的的一个实例,而子类重写了方法 test(),所以根据规则,这时应该调用子类的方法,所以输出“class B”。,多态性,多态性是面向对象技术中最灵活的特性,主要是增强项目的可扩展性,提高代码的可维护性。多态性依赖继承特性,可以把多态理解为继
24、承性的扩展或者深入。,继承结构的示例,/文件名:SuperClass.javapublic class SuperClasspublic void test()System.out.println(“SuperClass”); / 文件名:SubbClass1.javapublic class SubbClass1 extends SuperClasspublic void test()System.out.println(“SubbClass1”); / 文件名:SubbClass2.javapublic class SubbClass2 extends SuperClasspublic v
25、oid test()System.out.println(“SubbClass2”); ,对象类型的多态,对象类型的多态是指声明对象的类型不是对象的真正类型,而对象的真正类型由创建对象时调用的构造方法进行决定。例外,按照继承性的说明,子类的对象也是父类类型的对象,可以进行直接赋值。例如如下代码:SuperClass sc = new SubbClass1();这里声明了一个SuperClass类型的对象sc,然后使用SuperClass的子类SubbClass1的构造方法进行创建,因为子类类型的对象也是父类类型的对象,所以创建出来的对象可以直接赋值给父类类型的对象sc。除了对象的赋值以外,另外
26、一个更重要的知识是sc对象虽然使用SuperClass声明的类型,但是内部存储的却是SubbClass1类型的对象。这个可以Java语言的中instanceof运算符进行判断。instanceof是一个运算符,其作用是判断一个对象是否是某个类类型的对象,如果成立则表达式的值为true,否则为false。语法格式如下:对象名 instanceof 类名需要注意的是:这里的类名必须和声明对象时的类之间存储继承关系,否则将出现语法错误。,测试类型的代码,public class TestObjectType public static void main(String args) SuperClas
27、s sc = new SubbClass1();boolean b = sc instanceof SuperClass;boolean b1 = sc instanceof SubbClass1;System.out.println(b);System.out.println(b1);该测试程序的输出结果是:truetrue由程序运行结果可以看出,sc既是SuperClass类型的对象,也是SubbClass1类型的对象,而SubbClass1的类型被隐藏起来了,这就是对象的多态。其实sc对象不仅仅在类型上是SubbClass1类型的,其存储的内容也是SubbClass1的内容。,对象类型的
28、多态的用途,1. 对象的存储在存储一系列不同子类的对象时,可以使用父类的结构来进行声明,这样可以方便数据的存储,例如需要存储多个SubbClass1和SubbClass2的对象时,则可以声明一个SuperClass类型的数组进行存储,示例代码如下:SuperClass sc = new SuperClass3;sc0 = new SubbClass1();sc1 = new SubbClass2();sc2 = new SubbClass1();则这里的数组sc,可以存储各个类型子类的对象,而数组中每个元素的值都是存储的对应子类的对象,而只是在名义上的类型(语法上的类型)是SuperClass
29、类型的,这样将方便程序的控制,当增加新的子类类型时,已有的代码不需要进行改造就可以自动适应新的子类的结构。例如新增了一个SuperClass的子类SubbClass3,则该数组的代码可以修改成如下:SuperClass sc = new SuperClass3;sc0 = new SubbClass1();sc1 = new SubbClass2();sc2 = new SubbClass3();其它的代码都不需要进行修改,就可以适应新的结构,这是多态性最主要的用途。,2. 对象的传递在方法的传入参数传递,以及返回值处理方面都从对象类型的多态中受益。在向方法中传入参数时,如果该方法需要处理各个
30、子类的对象,则只需要书写一个接受父类类型对象的方法即可。例如:public void testObjectTypeMethod(SuperClass sc)则该在调用该方法时,可以传入SuperClass的对象,也可以传入其子类的对象,如果传入的是子类的对象,则子类对象中的内容不会丢失。例如调用的示例代码如下:SuperClass sc = new SuperClass();SubbClass1 sc1 = new SubbClass1();SubbClass2 sc2 = new SubbClass2();testObjectTypeMethod(sc); testObjectTypeMet
31、hod(sc1); testObjectTypeMethod(sc2);这里说明的只是调用时的语法结构,这样的特性将使我们只需要书写一个方法,就可以处理所有子类的对象,简化代码的书写,降低代码的重复,从而降低维护的难度。另外,方法的返回值也可以利用到该特性,例如如下方法:public SuperClass testObjectTypeMethod2()则在该方法的内部,既可以返回SuperClass类型的对象,也可以返回其子类的对象,也能简化代码的书写,便于代码的阅读和维护。,对象方法的多态,对象方法的多态基于方法的覆盖,也就是该对象调用的方法具体是子类的方法还是父类的方法,由创建对象时使用的
32、构造方法决定,而不是由声明对象时声明的类型决定。,public class TestObjectMethod public static void main(String args) SuperClass sc = new SuperClass(); SubbClass1 sc1 = new SubbClass1(); SubbClass2 sc2 = new SubbClass2(); SuperClass sc3 = new SubbClass1(); testObjectTypeMethod(sc); testObjectTypeMethod(sc1); testObjectTypeMe
33、thod(sc2); testObjectTypeMethod(sc3);public static void testObjectTypeMethod(SuperClass sc)sc.test(); /调用被覆盖的方法 该代码的执行结果如下:SuperClass SubbClass1 SubbClass2 SubbClass1则从代码的执行结果看,虽然testObjectTypeMethod方法接收的是SuperClass类型的对象,但是传入子类对象时,子类对象的内容没有丢失,所以在调用test方法时,还是调用的对应对象中对应的test方法。这样就在功能上实现了对象的传递,从而保留了对象的
34、内容,极大的方便了代码的扩展性。,动态调用过程,Vehicle,Truck5,Bus5,Car5,计算租金,每个子类重写了父类的计算租金的方法,父类中计算租金的方法的实现没有什么实际意义,只是起到“占位符”作用。,final修饰符,终极修饰符:(1)修饰对象是基本数据类型,认为该对象是常量,值不能被更改。(2)修饰对象是其他类的对象,认为该对象和其所指向的对象之间的关系是绑定的,不可更改,抽象类,抽象类是一种特殊的类,抽象类是使用 abstract 来声明的类。抽象类没有具体实例对象。抽象类的作用在于将许多有关的类组织在一起,提供一个公共的类,而那些被它组织在一起的具体的类作为它的子类由它派生
35、出来。抽象类描述公有行为的特征,并通过继承机制传送给它的子类,在抽象类派生的子类中必须实现抽象类中定义的所有的抽象方法。抽象类是对类的抽象。抽象类中一定存在无法实现的方法或者是没有实现的方法,所以抽象类是一种没有完全实现的类。抽象类例子水果是我们生活中常用到的一个事物类别,它就是一个抽象类。水果是可以吃的,但是当有人问你:水果怎么吃?你却无法回答。对于水果的吃法我们是无法描述的,只有当具体到是哪一类水果时,我们才知道它的吃法,如菠萝、葡萄、西瓜,它们的吃法各不相同。菠萝、葡萄、西瓜就是具体的类,水果是菠萝、葡萄、西瓜等的抽象。,抽象方法、抽象类,当父类的某些方法的实现并没有什么实际的意义时,可
36、以将该方法定义为抽象方法,即只定义该方法的头部,而不定义方法体(方法的实现)。1定义抽象方法只需在方法定义中加入关键字:abstract 如:public abstract void show();2.包含抽象方法的类必须被定义为抽象类程序演示:将Vehicle类改为使用抽象类实现,抽象类相关规则,1.含有抽象方法的类必须用abstract修饰,成为抽象类,抽象类中可以包含其他的非抽象方法;2.抽象类不能被实例化;3.如果从一个抽象类继承,而且想生成新类型的一个对象,就必须实现抽象基类中的每一个抽象方法,否则,派生类也必须使用abstract修饰成为抽象类。4.抽象类对象变量可以用来引用派生类的对象;,