面向对象:继承和多态.ppt

上传人:牧羊曲112 文档编号:6213426 上传时间:2023-10-05 格式:PPT 页数:43 大小:1.67MB
返回 下载 相关 举报
面向对象:继承和多态.ppt_第1页
第1页 / 共43页
面向对象:继承和多态.ppt_第2页
第2页 / 共43页
面向对象:继承和多态.ppt_第3页
第3页 / 共43页
面向对象:继承和多态.ppt_第4页
第4页 / 共43页
面向对象:继承和多态.ppt_第5页
第5页 / 共43页
点击查看更多>>
资源描述

《面向对象:继承和多态.ppt》由会员分享,可在线阅读,更多相关《面向对象:继承和多态.ppt(43页珍藏版)》请在三一办公上搜索。

1、Java程序设计,重庆交大,2023/10/5,上一章内容回顾,5.面向对象:构造方法,封装与隐藏构造器理解构造器构造器的重载封装理解封装访问控制符包(package)包及其作用package和import语句Java的常用包,2023/10/5,6.面向对象:继承和多态,6.1 类的继承6.1.1 继承的特点6.1.2 重写父类的方法6.1.3 父类实例的super引用6.1.4 调用父类的构造器6.2 多态6.2.1 多态性6.2.2 引用变量的强制类型转换6.2.3 instanceof运算符6.3 继承和组合6.4 课后作业,2023/10/5,6.1 类的继承6.1.1 继承的特点,

2、继承是面向对象三大特征之一,也是实现软件复用的重要手段。Java的继承通过关键字extends来实现,实现继承的类称为子类,被继承的类称为基类、超类、父类。父类与子类的关系,是一种一般和特殊的关系。例如水果和苹果的关系,苹果继承了水果,苹果是水果的子类,则苹果是一种特殊的水果。因为子类是一种特殊的父类,因此父类包含的范围总比子类包含的范围要大,因此父类是大类,子类是小类。Java的继承是单继承,每个类最多只有一个直接父类。,2023/10/5,6.1 类的继承6.1.1 继承的特点,Java里子类继承父类的语法格式如下:修饰符 class subclass extends superclass

3、/类定义部分“extends”的含义是子类扩展了父类,将可以获得父类的全部属性和方法,但子类不能获得父类构造方法。以下程序示范了子类继承父类的特点。,2023/10/5,6.1 类的继承6.1.1 继承的特点,程序清单:chapter06test1Fruit.java、Apple.java,package chapter06.test1;public class Fruit public double weight;public void info()System.out.println(我是一个水果!重+weight+g!);,package chapter06.test1;public c

4、lass Apple extends Fruit public static void main(String args)/创建Apple的对象 Apple a=new Apple();/Apple对象本身没有weight属性。/因为Apple的父类有weight属性,也可以访问Apple对象的属性 a.weight=56;/调用Apple对象的info方法 a.info();,该程序的输出结果为:我是一个水果!重56.0g!,2023/10/5,6.1 类的继承6.1.1 继承的特点,2023/10/5,6.1 类的继承6.1.2 重写父类的方法,子类扩展了父类,子类是一个特殊的父类。大部分

5、时候,子类总是以父类为基础,额外增加新的属性和方法。但有一种情况例外:子类需要重写父类的方法。例如,鸟类都包含了飞翔(fly)的方法,其中鸵鸟是一种特殊的鸟类,因此鸵鸟应该是鸟的子类,因此它也将从鸟类获得飞翔方法,但这个飞翔方法明显不适合鸵鸟,因此,鸵鸟需要重写鸟类的方法。下面程序先定义一个Bird类。,2023/10/5,6.1 类的继承6.1.2 重写父类的方法,程序清单:chapter06test1Bird.java,package chapter06.test1;public class Bird/Bird类的fly方法 public void fly()System.out.prin

6、tln(我在天空里自由自在地飞翔.);,2023/10/5,6.1 类的继承6.1.2 重写父类的方法,下面再定义一个Ostrich类,这个类扩展了Bird类,但重写了Bird类的fly方法。程序清单:chapter06test1Ostrich.java,package chapter06.test1;public class Ostrich extends Bird/重写Bird类的fly方法 public void fly()System.out.println(我只能在地上奔跑.);public static void main(String args)/创建Ostrich对象 Ostr

7、ich os=new Ostrich();/执行Ostrich对象的fly方法,将输出我只能在地上奔跑.os.fly();,该程序的输出结果为:我只能在地上奔跑.,2023/10/5,6.1 类的继承6.1.2 重写父类的方法,这种子类包含父类同名方法的现象被称为方法重写,也称为方法覆盖(Override)。可以说子类重写了父类的方法,也可以说子类覆盖了父类的方法。方法的重写要遵循“两同两小一大”。两同:方法名相同;形参列表相同。两小:子类方法返回值类型应比父类方法返回值类型更小(即子类)或相同;子类方法声明抛出的异常应比父类方法声明抛出的异常类更小或相同。一大:子类方法的访问控制权限应比父类

8、方法更大或相等。,2023/10/5,6.1 类的继承6.1.2 重写父类的方法,注意:覆盖方法和被覆盖方法要么都是类方法,要么都是实例方法;不能一个是类方法,一个是实例方法,否则编译出错。当子类覆盖了父类方法后,子类的对象将无法直接访问父类中被覆盖的方法,如果需要访问,可以使用super(被覆盖的是实例方法)或者父类名(被覆盖的是类方法)作为调用者来调用父类被覆盖的方法。如果父类方法具有private访问权限,则该方法对其子类是隐藏的,因此其子类无法访问该方法,也就无法重写该方法;如果子类定义了一个与父类private方法具有相同方法名、相同形参列表、相同返回值类型的方法,依然不是重写,只是

9、在子类中重新定义了一个新方法。,2023/10/5,6.1 类的继承 父类实例的super引用,(1)通过super引用调用父类被覆盖的方法如果需要在子类中调用父类被覆盖的实例方法,可以通过关键字super作为调用者来调用父类被覆盖的方法。super是Java提供的一个关键字,它是直接父类的默认引用。例如,为上面的Ostrich类添加callOverridedMethod方法,在其中调用Bird类被覆盖的fly方法。完整的Ostrich类代码如下。程序清单:chapter06test1Ostrich.java,2023/10/5,6.1 类的继承 父类实例的super引用,package ch

10、apter06.test1;public class Ostrich extends Bird/重写Bird类的fly方法 public void fly()System.out.println(我只能在地上奔跑.);public void callOverridedMethod()/在子类方法中通过super来显式调用父类被覆盖的方法。super.fly();public static void main(String args)/创建Ostrich对象 Ostrich os=new Ostrich();/执行Ostrich对象的fly方法,将输出我只能在地上奔跑.os.fly();os.c

11、allOverridedMethod();,该程序的输出结果为:我只能在地上奔跑.我在天空里自由自在地飞翔.,2023/10/5,6.1 类的继承 父类实例的super引用,2023/10/5,6.1 类的继承 父类实例的super引用,2023/10/5,6.1 类的继承 父类实例的super引用,(2)通过super引用访问父类的属性如果子类定义了和父类同名的属性,也会发生子类属性覆盖父类属性的情形。正常情况下,子类里定义的方法访问该属性,都是访问子类属性,无法访问到父类被覆盖的属性。但在子类定义的实例方法中可以通过super引用来访问父类被覆盖的属性。详见下面的例子程序清单:chapte

12、r06SubClass.java,2023/10/5,6.1 类的继承 父类实例的super引用,package chapter06;class BaseClass public int a=5;public class SubClass extends BaseClass public int a=7;public void accessOwner()System.out.println(a);public void accessBase()/通过super来访问方法调用者对应的父类对象 System.out.println(super.a);public static void main(

13、String args)SubClass sc=new SubClass();/直接访问SubClass对象的a属性将会输出7 System.out.println(sc.a);sc.accessOwner();/输出7 sc.accessBase();/输出5,该程序的输出结果为:775,2023/10/5,6.1 类的继承 调用父类的构造器,(1)通过super引用调用父类的构造器在一个构造器中调用另一个重载的构造器要使用this引用来调用。在子类构造器中调用父类构造器要使用super引用来调用。详见下面的例子。程序清单:chapter06test1Sub.java,2023/10/5,6

14、.1 类的继承 调用父类的构造器,package chapter06.test1;class Base public double size;public String name;public Base(double size,String name)this.size=size;this.name=name;,public class Sub extends Base public String color;public Sub(double size,String name,String color)/通过super调用来调用父类构造器的初始化过程 super(size,name);thi

15、s.color=color;public static void main(String args)Sub s=new Sub(5.6,测试对象,红色);/输出Sub对象的三个属性 System.out.println(s.size+-+s.name+-+s.color);,该程序的输出结果为:5.6-测试对象-红色,2023/10/5,6.1 类的继承 调用父类的构造器,(2)构造器的调用顺序在调用子类的构造器创建一个子类实例时,父类构造器总会在子类构造器之前执行;不仅如此,执行父类构造器时,系统会再次上溯执行其父类的构造器,;以此类推,创建任何Java对象,最先执行的总是类的构造器。详见下

16、面的例子。程序清单:chapter06test1Wolf.java,2023/10/5,6.1 类的继承 调用父类的构造器,package chapter06.test1;class Creature public Creature()System.out.println(Creature无参数的构造器);class Animal extends Creature public Animal(String name)System.out.println(Animal带一个参数的构造器,该动物的name为+name);public Animal(String name,int age)/使用th

17、is调用同一个重载的构造器 this(name);System.out.println(Animal带2个参数的构造器,其age为+age);,public class Wolf extends Animal public Wolf()/显式调用父类有2个参数的构造器 super(土狼,3);System.out.println(Wolf无参数的构造器);public static void main(String args)new Wolf();,该程序的输出结果为:Creature无参数的构造器Animal带一个参数的构造器,该动物的name为土狼Animal带2个参数的构造器,其age为

18、3Wolf无参数的构造器,2023/10/5,6.2 多态,Java引用变量有两个类型:一个是编译时的类型,一个是运行时的类型,编译时的类型由声明该变量时使用的类型决定,运行时的类型由实际赋给该变量的对象决定。如果编译时类型和运行时的类型不一致,这就有可能出现所谓的多态(Polymorphism)。,2023/10/5,6.2 多态6.2.1 多态性,先看以下例子。程序清单:chapter06test2SubClass.java,package chapter06.test2;class BaseClass public int book=6;public void base()System.

19、out.println(父类的普通方法);public void test()System.out.println(父类的被覆盖的方法);,public class SubClass extends BaseClass/重新定义一个book实例属性覆盖父类的book实例属性 public String book=轻量级J2EE企业应用实战;public void test()System.out.println(子类的覆盖父类的方法);public void sub()System.out.println(子类的普通方法);public static void main(String args

20、)/下面编译时类型和运行时类型完全一样,因此不存在多态 BaseClass bc=new BaseClass();/输出 6 System.out.println(bc.book);/下面两次调用将执行BaseClass的方法 bc.base();bc.test();,该程序的输出结果为:6父类的普通方法父类的被覆盖的方法轻量级J2EE企业应用实战父类的普通方法子类的覆盖父类的方法子类的普通方法6父类的普通方法子类的覆盖父类的方法,2023/10/5,6.2 多态6.2.1 多态性,package chapter06.test2;class BaseClass public int book=

21、6;public void base()System.out.println(父类的普通方法);public void test()System.out.println(父类的被覆盖的方法);,/下面编译时类型和运行时类型完全一样,因此不存在多态 SubClass sc=new SubClass();/输出轻量级J2EE企业应用实战 System.out.println(sc.book);/下面调用将执行从父类继承到的base方法 sc.base();/下面调用将执行从当前类的test方法 sc.test();/下面调用将执行从当前类的sub方法 sc.sub();/下面编译时类型和运行时类型

22、不一样,多态发生 BaseClass ploymophicBc=new SubClass();/输出 6 表明访问的是父类属性 System.out.println(ploymophicBc.book);/下面调用将执行从父类继承到的base方法 ploymophicBc.base();/下面调用将执行当前类的test方法 ploymophicBc.test();/因为ploymophicBc的编译类型是BaseClass,BaseClass类没有/提供sub方法,所以下面代码编译时会出现错误。/ploymophicBc.sub();,该程序的输出结果为:6父类的普通方法父类的被覆盖的方法轻量

23、级J2EE企业应用实战父类的普通方法子类的覆盖父类的方法子类的普通方法6父类的普通方法子类的覆盖父类的方法,2023/10/5,6.2 多态6.2.1 多态性,上述例子中,第三个引用变量polymopicBc比较特殊,它的编译时类型是BaseClass,而运行时类型是SubClass,当调用该引用变量的test方法(BaseClass类定义了该方法,子类SubClass覆盖了父类的该方法),实际执行的是SubClass类中覆盖后的test方法,这就是多态。因为子类其实是一种特殊的父类,因此Java允许把一个子类对象直接赋给一个父类引用变量,无须任何类型转换,或者被称为向上转型(upcastin

24、g)、或上溯。向上转型由系统自动完成。,/下面编译时类型和运行时类型不一样,多态发生BaseClass ploymophicBc=new SubClass();,2023/10/5,6.2 多态6.2.1 多态性,2023/10/5,6.2 多态6.2.1 多态性,当把一个子类对象直接赋给父类引用变量,例如上述例子中的语句“BaseClass ploymophicBc=new SubClass();”,这个ploymophicBc引用变量的编译时类型是BaseClass,而运行时类型是SubClass,当运行时调用该引用变量的方法时,其方法行为总是像子类方法的行为,而不是像父类方法的行为,这将

25、出现相同类型的变量、执行同一个方法时呈现出不同的行为特征,这就是多态。,2023/10/5,6.2 多态6.2.2 引用变量的强制类型转换,C语言里也有强制类型转换。在Java程序里,引用变量只能调用它编译时类型的方法,而不能调用它运行时类型的方法(详见节中被注释的代码),即使它实际所引用对象确实包含该方法。如果需要让这个引用变量来调用它运行时类型的方法,则必须把它强制类型转换成运行时类型。强制类型转换运算符是小括号,语法如下:(type)variable;,2023/10/5,6.2 多态6.2.2 引用变量的强制类型转换,Java语言里的强制类型转换运算要注意:基本类型之间的转换只能在数值

26、类型之间进行,这里所说的数值类型包括整数型、字符型和浮点型。但数值型不能和布尔型之间进行类型转换。引用类型之间的转换只能把一个父类变量转换成子类类型,如果是两个没有任何继承关系的类型,则无法进行类型转换,否则编译时就会出现错误。如果试图把一个父类实例转换成子类类型,则这个实例必须实际上是子类实例才行(即编译时类型为父类类型,而运行时类型是子类类型),否则将会在运行时引发ClassCastException异常。下面是进行强制类型转换的示范程序,下面程序详细说明了哪些情况可以进行类型转换,哪些情况不可以进行类型转换。,2023/10/5,6.2 多态6.2.2 引用变量的强制类型转换,程序清单:

27、chapter06test2TestConversion.java,package chapter06.test2;public class TestConversion public static void main(String args)double d=13.4;long l=(long)d;System.out.println(l);int in=5;/下面代码编译时出错:试图把一个数值型变量转换为boolean型,/编译时候会提示:不可转换的类型/boolean b=(boolean)in;Object obj=Hello;/obj变量的编译类型为Object,是String类型的

28、父类,可以强制类型转换/而且obj变量实际上类型也是String类型,所以运行时也可通过 String objStr=(String)obj;System.out.println(objStr);/定义一个objPri变量,编译类型为Object,实际类型为Integer Object objPri=new Integer(5);/objPri变量的编译类型为Object,是String类的父类,可以强制转换/而objPri变量实际上类型是Integer类型,所以下面代码运行时引发异常/String str=(String)objPri;,该程序的输出结果为:13Hello,2023/10/5

29、,6.2 多态6.2.2 引用变量的强制类型转换,考虑到进行强制类型转换时可能会出现异常,因此进行类型转换前应先通过instanceof运算符来判断是否可以成功转换。例如上面的代码“String str=(String)objPri;”运行时会引发ClassCastException异常,这是因为objPri不可转换成String类型,为了让程序更健壮,可以将代码修改为:if(objPri instanceof String)String str=(String)objPri;在进行强制类型转换前,先用instanceof运算符判断是否可以成功转换,从而避免出现ClassCastExcepti

30、on异常。,2023/10/5,6.2 多态6.2.3 instanceof运算符,instanceof和类型转换运算符一样,都是Java提供的运算符。其语法格式为:引用变量 instanceof 类(或接口)instanceof运算符的左操作数通常是一个引用类型的变量,右操作数通常是一个类(也可以是接口),它用于判断左边的对象是否是右边类(或者其子类)的实例。如果是返回true,否则返回false。下面的程序示范了instanceof运算符的用法。程序清单:chapter06test2TestInstanceof.java,2023/10/5,6.2 多态6.2.3 instanceof运算

31、符,package chapter06.test2;public class TestInstanceof public static void main(String args)/声明hello为Object类,则hello的编译类型是Object,Object是所有类的父类/但hello变量的实际类型是String Object hello=Hello;/String是Object类的子类,所以返回true。(字符串是否是Object类的实例:+(hello instanceof Object);/返回true。(字符串是否是String类的实例:+(hello instanceof St

32、ring);/返回false。(字符串是否是Math类的实例:+(hello instanceof Math);/String实现了Comparable接口,所以返回true。(字符串是否是Comparable接口的实例:+(hello instanceof Comparable);String a=Hello;/String类既不是Math类,也不是Math类的父类,所以下面代码编译通不过/(字符串是否是Math类的实例:+(a instanceof Math);,该程序的输出结果为:字符串是否是Object类的实例:true字符串是否是String类的实例:true字符串是否是Math类的

33、实例:false字符串是否是Comparable接口的实例:true,2023/10/5,6.3 继承和组合6.3.1 使用继承的注意事项,继承是实现类重用的重要手段,但继承也带来了一个最大的坏处:破坏封装。组合是实现类重用的另一种方式。下面介绍继承和组合之间的练习和区别。子类扩展父类时,子类将可以从父类继承得到属性和方法,如果访问权限允许,子类将可以直接访问父类的属性和方法,相当于子类可以直接复用父类的属性和方法,确实非常方便。继承带来了高度复用的同时,也带来了一个严重的问题:继承严重地破坏了父类的封装性。前面介绍封装时提到:每个类都应该封装它内部信息和实现细节,而只暴露必要的方法给其他类使

34、用。但在继承关系中,子类可以直接访问父类的属性和方法,从而造成子类和父类的严重耦合。,2023/10/5,6.3 继承和组合6.3.1 使用继承的注意事项,为了保证父类良好的封装性,不会被子类随意改变,设计父类通常应该遵循如下规则:尽量隐藏父类的内部数据。不要让子类可以随意访问、修改父类方法。尽量不要在父类构造器中调用将要被子类重写的方法。如果想把某些类设置成最终类,即不再派生出子类,则可以用final修饰这个类。到底何时需要从父类派生新的子类?不仅需要保证子类是一种特殊的父类,而且还需要具备以下两个条件之一:子类需要额外增加属性,而不仅仅是属性值的改变。子类需要增加自己独有的行为方式(包括增

35、加新的方法或重写父类的方法)。,2023/10/5,6.3 继承和组合6.3.2 利用组合实现复用,继承要表达的是一种“是(is-a)”的关系,而组合表达的是“有(has-a)”的关系。【提问】试从现实生活中例举继承和组合的例子。假设有三个类:Animal、Wolf和Bird,它们之间有如下图所示的继承树。,2023/10/5,6.3 继承和组合6.3.2 利用组合实现复用,程序清单:chapter06test3TestInherit.java,package chapter06.test3;class Animal private void beat()(心脏跳动.);public void

36、 breath()beat();(吸一口气,吐一口气,呼吸中.);/继承Animal,直接复用父类的breath方法class Bird extends Animal public void fly()(我在天空自在的飞翔.);,/继承Animal,直接复用父类的breath方法class Wolf extends Animal public void run()(我在陆地上的快速奔跑.);public class TestInherit public static void main(String args)Bird b=new Bird();b.breath();b.fly();Wolf

37、w=new Wolf();w.breath();w.run();,2023/10/5,6.3 继承和组合6.3.2 利用组合实现复用,如果仅从软件复用的角度,将上面三个类的定义改为如下形式也可实现相同的复用。程序清单:chapter06TestComposite.java,2023/10/5,6.3 继承和组合6.3.2 利用组合实现复用,package chapter06;class Animal private void beat()System.out.println(心脏跳动.);public void breath()beat();System.out.println(吸一口气,吐一

38、口气,呼吸中.);,class Bird/将原来的父类嵌入原来的子类,作为子类的一个组合成分 private Animal a;public Bird(Animal a)this.a=a;/重新定义一个自己的breath方法 public void breath()/直接复用Animal提供的breath方法来实现Bird的breath方法。a.breath();public void fly()System.out.println(我在天空自在的飞翔.);,class Wolf/将原来的父类嵌入原来的子类,作为子类的一个组合成分 private Animal a;public Wolf(An

39、imal a)this.a=a;/重新定义一个自己的breath方法 public void breath()/直接复用Animal提供的breath方法来实现Bird的breath方法 a.breath();public void run()System.out.println(我在陆地上的快速奔跑.);,public class TestComposite public static void main(String args)/此时需要显式创建被嵌入的对象 Animal a1=new Animal();Bird b=new Bird(a1);b.breath();b.fly();/此时需

40、要显式创建被嵌入的对象 Animal a2=new Animal();Wolf w=new Wolf(a2);w.breath();w.run();,2023/10/5,6.3 继承和组合6.3.2 利用组合实现复用,对于上面定义的三个类:Animal、Wolf和Bird,它们对应的UML图如下图所示。,2023/10/5,6.4 课后作业,1.(面向对象设计)某项目涉及到的实体包括:乐器(instrument):包含名称(name)和品牌(brand)属性,具有弹奏(play)方法。钢琴(Piano):一种特殊的乐器,新增按键数(keyNum)属性,具有特有的弹奏(play)方法。小提琴(Violin):一种特殊的乐器,新增琴弦数(chordNum)属性,具有特有的弹奏(play)方法。吉他(Guitar):一种特殊的乐器,新增按键数(keyNum)属性,具有特有的弹奏(play)方法。手风琴(Accordion):一种特殊的乐器,新增琴弦数(chordNum)属性,具有特有的弹奏(play)方法。上述5个类的类图如下图所示,请用Java代码实现这5个类,(方法的实现只需输出提示信息即可);另外再新建一个Test类,用于测试这5个类,要求充分测试多态。,2023/10/5,6.4 课后作业,

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 生活休闲 > 在线阅读


备案号:宁ICP备20000045号-2

经营许可证:宁B2-20210002

宁公网安备 64010402000987号