面向对象:继承和多态.ppt_第1页
面向对象:继承和多态.ppt_第2页
面向对象:继承和多态.ppt_第3页
面向对象:继承和多态.ppt_第4页
面向对象:继承和多态.ppt_第5页
已阅读5页,还剩38页未读 继续免费阅读

下载本文档

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

文档简介

Java程序设计 重庆交大 23 05 上一章内容回顾 5 面向对象 构造方法 封装与隐藏构造器理解构造器构造器的重载封装理解封装访问控制符包 package 包及其作用package和import语句Java的常用包 23 05 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 3instanceof运算符6 3继承和组合6 4课后作业 23 05 6 1类的继承6 1 1继承的特点 继承是面向对象三大特征之一 也是实现软件复用的重要手段 Java的继承通过关键字extends来实现 实现继承的类称为子类 被继承的类称为基类 超类 父类 父类与子类的关系 是一种一般和特殊的关系 例如水果和苹果的关系 苹果继承了水果 苹果是水果的子类 则苹果是一种特殊的水果 因为子类是一种特殊的父类 因此父类包含的范围总比子类包含的范围要大 因此父类是大类 子类是小类 Java的继承是单继承 每个类最多只有一个直接父类 23 05 6 1类的继承6 1 1继承的特点 Java里子类继承父类的语法格式如下 修饰符 classsubclassextendssuperclass 类定义部分 extends 的含义是子类扩展了父类 将可以获得父类的全部属性和方法 但子类不能获得父类构造方法 以下程序示范了子类继承父类的特点 23 05 6 1类的继承6 1 1继承的特点 程序清单 chapter06 test1 Fruit java Apple java packagechapter06 test1 publicclassFruit publicdoubleweight publicvoidinfo System out println 我是一个水果 重 weight g packagechapter06 test1 publicclassAppleextendsFruit publicstaticvoidmain String args 创建Apple的对象Applea newApple Apple对象本身没有weight属性 因为Apple的父类有weight属性 也可以访问Apple对象的属性a weight 56 调用Apple对象的info方法a info 该程序的输出结果为 我是一个水果 重56 0g 23 05 6 1类的继承6 1 1继承的特点 23 05 6 1类的继承6 1 2重写父类的方法 子类扩展了父类 子类是一个特殊的父类 大部分时候 子类总是以父类为基础 额外增加新的属性和方法 但有一种情况例外 子类需要重写父类的方法 例如 鸟类都包含了飞翔 fly 的方法 其中鸵鸟是一种特殊的鸟类 因此鸵鸟应该是鸟的子类 因此它也将从鸟类获得飞翔方法 但这个飞翔方法明显不适合鸵鸟 因此 鸵鸟需要重写鸟类的方法 下面程序先定义一个Bird类 23 05 6 1类的继承6 1 2重写父类的方法 程序清单 chapter06 test1 Bird java packagechapter06 test1 publicclassBird Bird类的fly方法publicvoidfly System out println 我在天空里自由自在地飞翔 23 05 6 1类的继承6 1 2重写父类的方法 下面再定义一个Ostrich类 这个类扩展了Bird类 但重写了Bird类的fly方法 程序清单 chapter06 test1 Ostrich java packagechapter06 test1 publicclassOstrichextendsBird 重写Bird类的fly方法publicvoidfly System out println 我只能在地上奔跑 publicstaticvoidmain String args 创建Ostrich对象Ostrichos newOstrich 执行Ostrich对象的fly方法 将输出 我只能在地上奔跑 os fly 该程序的输出结果为 我只能在地上奔跑 23 05 6 1类的继承6 1 2重写父类的方法 这种子类包含父类同名方法的现象被称为方法重写 也称为方法覆盖 Override 可以说子类重写了父类的方法 也可以说子类覆盖了父类的方法 方法的重写要遵循 两同两小一大 两同 方法名相同 形参列表相同 两小 子类方法返回值类型应比父类方法返回值类型更小 即子类 或相同 子类方法声明抛出的异常应比父类方法声明抛出的异常类更小或相同 一大 子类方法的访问控制权限应比父类方法更大或相等 23 05 6 1类的继承6 1 2重写父类的方法 注意 覆盖方法和被覆盖方法要么都是类方法 要么都是实例方法 不能一个是类方法 一个是实例方法 否则编译出错 当子类覆盖了父类方法后 子类的对象将无法直接访问父类中被覆盖的方法 如果需要访问 可以使用super 被覆盖的是实例方法 或者父类名 被覆盖的是类方法 作为调用者来调用父类被覆盖的方法 如果父类方法具有private访问权限 则该方法对其子类是隐藏的 因此其子类无法访问该方法 也就无法重写该方法 如果子类定义了一个与父类private方法具有相同方法名 相同形参列表 相同返回值类型的方法 依然不是重写 只是在子类中重新定义了一个新方法 23 05 6 1类的继承6 1 3父类实例的super引用 1 通过super引用调用父类被覆盖的方法如果需要在子类中调用父类被覆盖的实例方法 可以通过关键字super作为调用者来调用父类被覆盖的方法 super是Java提供的一个关键字 它是直接父类的默认引用 例如 为上面的Ostrich类添加callOverridedMethod方法 在其中调用Bird类被覆盖的fly方法 完整的Ostrich类代码如下 程序清单 chapter06 test1 Ostrich java 23 05 6 1类的继承6 1 3父类实例的super引用 packagechapter06 test1 publicclassOstrichextendsBird 重写Bird类的fly方法publicvoidfly System out println 我只能在地上奔跑 publicvoidcallOverridedMethod 在子类方法中通过super来显式调用父类被覆盖的方法 super fly publicstaticvoidmain String args 创建Ostrich对象Ostrichos newOstrich 执行Ostrich对象的fly方法 将输出 我只能在地上奔跑 os fly os callOverridedMethod 该程序的输出结果为 我只能在地上奔跑 我在天空里自由自在地飞翔 23 05 6 1类的继承6 1 3父类实例的super引用 23 05 6 1类的继承6 1 3父类实例的super引用 23 05 6 1类的继承6 1 3父类实例的super引用 2 通过super引用访问父类的属性如果子类定义了和父类同名的属性 也会发生子类属性覆盖父类属性的情形 正常情况下 子类里定义的方法访问该属性 都是访问子类属性 无法访问到父类被覆盖的属性 但在子类定义的实例方法中可以通过super引用来访问父类被覆盖的属性 详见下面的例子程序清单 chapter06 SubClass java 23 05 6 1类的继承6 1 3父类实例的super引用 packagechapter06 classBaseClass publicinta 5 publicclassSubClassextendsBaseClass publicinta 7 publicvoidaccessOwner System out println a publicvoidaccessBase 通过super来访问方法调用者对应的父类对象System out println super a publicstaticvoidmain String args SubClasssc newSubClass 直接访问SubClass对象的a属性将会输出7System out println sc a sc accessOwner 输出7sc accessBase 输出5 该程序的输出结果为 775 23 05 6 1类的继承6 1 4调用父类的构造器 1 通过super引用调用父类的构造器在一个构造器中调用另一个重载的构造器要使用this引用来调用 在子类构造器中调用父类构造器要使用super引用来调用 详见下面的例子 程序清单 chapter06 test1 Sub java 23 05 6 1类的继承6 1 4调用父类的构造器 packagechapter06 test1 classBase publicdoublesize publicStringname publicBase doublesize Stringname this size size this name name publicclassSubextendsBase publicStringcolor publicSub doublesize Stringname Stringcolor 通过super调用来调用父类构造器的初始化过程super size name this color color publicstaticvoidmain String args Subs newSub 5 6 测试对象 红色 输出Sub对象的三个属性System out println s size s name s color 该程序的输出结果为 5 6 测试对象 红色 23 05 6 1类的继承6 1 4调用父类的构造器 2 构造器的调用顺序在调用子类的构造器创建一个子类实例时 父类构造器总会在子类构造器之前执行 不仅如此 执行父类构造器时 系统会再次上溯执行其父类的构造器 以此类推 创建任何Java对象 最先执行的总是java lang Object类的构造器 详见下面的例子 程序清单 chapter06 test1 Wolf java 23 05 6 1类的继承6 1 4调用父类的构造器 packagechapter06 test1 classCreature publicCreature System out println Creature无参数的构造器 classAnimalextendsCreature publicAnimal Stringname System out println Animal带一个参数的构造器 该动物的name为 name publicAnimal Stringname intage 使用this调用同一个重载的构造器this name System out println Animal带2个参数的构造器 其age为 age publicclassWolfextendsAnimal publicWolf 显式调用父类有2个参数的构造器super 土狼 3 System out println Wolf无参数的构造器 publicstaticvoidmain String args newWolf 该程序的输出结果为 Creature无参数的构造器Animal带一个参数的构造器 该动物的name为土狼Animal带2个参数的构造器 其age为3Wolf无参数的构造器 23 05 6 2多态 Java引用变量有两个类型 一个是编译时的类型 一个是运行时的类型 编译时的类型由声明该变量时使用的类型决定 运行时的类型由实际赋给该变量的对象决定 如果编译时类型和运行时的类型不一致 这就有可能出现所谓的多态 Polymorphism 23 05 6 2多态6 2 1多态性 先看以下例子 程序清单 chapter06 test2 SubClass java packagechapter06 test2 classBaseClass publicintbook 6 publicvoidbase System out println 父类的普通方法 publicvoidtest System out println 父类的被覆盖的方法 publicclassSubClassextendsBaseClass 重新定义一个book实例属性覆盖父类的book实例属性publicStringbook 轻量级J2EE企业应用实战 publicvoidtest System out println 子类的覆盖父类的方法 publicvoidsub System out println 子类的普通方法 publicstaticvoidmain String args 下面编译时类型和运行时类型完全一样 因此不存在多态BaseClassbc newBaseClass 输出6System out println bc book 下面两次调用将执行BaseClass的方法bc base bc test 该程序的输出结果为 6父类的普通方法父类的被覆盖的方法轻量级J2EE企业应用实战父类的普通方法子类的覆盖父类的方法子类的普通方法6父类的普通方法子类的覆盖父类的方法 23 05 6 2多态6 2 1多态性 packagechapter06 test2 classBaseClass publicintbook 6 publicvoidbase System out println 父类的普通方法 publicvoidtest System out println 父类的被覆盖的方法 下面编译时类型和运行时类型完全一样 因此不存在多态SubClasssc newSubClass 输出 轻量级J2EE企业应用实战 System out println sc book 下面调用将执行从父类继承到的base方法sc base 下面调用将执行从当前类的test方法sc test 下面调用将执行从当前类的sub方法sc sub 下面编译时类型和运行时类型不一样 多态发生BaseClassploymophicBc newSubClass 输出6 表明访问的是父类属性System out println ploymophicBc book 下面调用将执行从父类继承到的base方法ploymophicBc base 下面调用将执行当前类的test方法ploymophicBc test 因为ploymophicBc的编译类型是BaseClass BaseClass类没有 提供sub方法 所以下面代码编译时会出现错误 ploymophicBc sub 该程序的输出结果为 6父类的普通方法父类的被覆盖的方法轻量级J2EE企业应用实战父类的普通方法子类的覆盖父类的方法子类的普通方法6父类的普通方法子类的覆盖父类的方法 23 05 6 2多态6 2 1多态性 上述例子中 第三个引用变量polymopicBc比较特殊 它的编译时类型是BaseClass 而运行时类型是SubClass 当调用该引用变量的test方法 BaseClass类定义了该方法 子类SubClass覆盖了父类的该方法 实际执行的是SubClass类中覆盖后的test方法 这就是多态 因为子类其实是一种特殊的父类 因此Java允许把一个子类对象直接赋给一个父类引用变量 无须任何类型转换 或者被称为向上转型 upcasting 或上溯 向上转型由系统自动完成 下面编译时类型和运行时类型不一样 多态发生BaseClassploymophicBc newSubClass 23 05 6 2多态6 2 1多态性 23 05 6 2多态6 2 1多态性 当把一个子类对象直接赋给父类引用变量 例如上述例子中的语句 BaseClassploymophicBc newSubClass 这个ploymophicBc引用变量的编译时类型是BaseClass 而运行时类型是SubClass 当运行时调用该引用变量的方法时 其方法行为总是像子类方法的行为 而不是像父类方法的行为 这将出现相同类型的变量 执行同一个方法时呈现出不同的行为特征 这就是多态 23 05 6 2多态6 2 2引用变量的强制类型转换 C语言里也有强制类型转换 在Java程序里 引用变量只能调用它编译时类型的方法 而不能调用它运行时类型的方法 详见6 2 1节中被注释的代码 即使它实际所引用对象确实包含该方法 如果需要让这个引用变量来调用它运行时类型的方法 则必须把它强制类型转换成运行时类型 强制类型转换运算符是小括号 语法如下 type variable 23 05 6 2多态6 2 2引用变量的强制类型转换 Java语言里的强制类型转换运算要注意 基本类型之间的转换只能在数值类型之间进行 这里所说的数值类型包括整数型 字符型和浮点型 但数值型不能和布尔型之间进行类型转换 引用类型之间的转换只能把一个父类变量转换成子类类型 如果是两个没有任何继承关系的类型 则无法进行类型转换 否则编译时就会出现错误 如果试图把一个父类实例转换成子类类型 则这个实例必须实际上是子类实例才行 即编译时类型为父类类型 而运行时类型是子类类型 否则将会在运行时引发ClassCastException异常 下面是进行强制类型转换的示范程序 下面程序详细说明了哪些情况可以进行类型转换 哪些情况不可以进行类型转换 23 05 6 2多态6 2 2引用变量的强制类型转换 程序清单 chapter06 test2 TestConversion java packagechapter06 test2 publicclassTestConversion publicstaticvoidmain String args doubled 13 4 longl long d System out println l intin 5 下面代码编译时出错 试图把一个数值型变量转换为boolean型 编译时候会提示 不可转换的类型 booleanb boolean in Objectobj Hello obj变量的编译类型为Object 是String类型的父类 可以强制类型转换 而且obj变量实际上类型也是String类型 所以运行时也可通过StringobjStr String obj System out println objStr 定义一个objPri变量 编译类型为Object 实际类型为IntegerObjectobjPri newInteger 5 objPri变量的编译类型为Object 是String类的父类 可以强制转换 而objPri变量实际上类型是Integer类型 所以下面代码运行时引发异常 Stringstr String objPri 该程序的输出结果为 13Hello 23 05 6 2多态6 2 2引用变量的强制类型转换 考虑到进行强制类型转换时可能会出现异常 因此进行类型转换前应先通过instanceof运算符来判断是否可以成功转换 例如上面的代码 Stringstr String objPri 运行时会引发ClassCastException异常 这是因为objPri不可转换成String类型 为了让程序更健壮 可以将代码修改为 if objPriinstanceofString Stringstr String objPri 在进行强制类型转换前 先用instanceof运算符判断是否可以成功转换 从而避免出现ClassCastException异常 23 05 6 2多态6 2 3instanceof运算符 instanceof和类型转换运算符一样 都是Java提供的运算符 其语法格式为 引用变量instanceof类 或接口 instanceof运算符的左操作数通常是一个引用类型的变量 右操作数通常是一个类 也可以是接口 它用于判断左边的对象是否是右边类 或者其子类 的实例 如果是返回true 否则返回false 下面的程序示范了instanceof运算符的用法 程序清单 chapter06 test2 TestInstanceof java 23 05 6 2多态6 2 3instanceof运算符 packagechapter06 test2 publicclassTestInstanceof publicstaticvoidmain String args 声明hello为Object类 则hello的编译类型是Object Object是所有类的父类 但hello变量的实际类型是StringObjecthello Hello String是Object类的子类 所以返回true System out println 字符串是否是Object类的实例 helloinstanceofObject 返回true System out println 字符串是否是String类的实例 helloinstanceofString 返回false System out println 字符串是否是Math类的实例 helloinstanceofMath String实现了Comparable接口 所以返回true System out println 字符串是否是Comparable接口的实例 helloinstanceofComparable Stringa Hello String类既不是Math类 也不是Math类的父类 所以下面代码编译通不过 System out println 字符串是否是Math类的实例 ainstanceofMath 该程序的输出结果为 字符串是否是Object类的实例 true字符串是否是String类的实例 true字符串是否是Math类的实例 false字符串是否是Comparable接口的实例 true 23 05 6 3继承和组合6 3 1使用继承的注意事项 继承是实现类重用的重要手段 但继承也带来了一个最大的坏处 破坏封装 组合是实现类重用的另一种方式 下面介绍继承和组合之间的练习和区别 子类扩展父类时 子类将可以从父类继承得到属性和方法 如果访问权限允许 子类将可以直接访问父类的属性和方法 相当于子类可以直接复用父类的属性和方法 确实非常方便 继承带来了高度复用的同时 也带来了一个严重的问题 继承严重地破坏了父类的封装性 前面介绍封装时提到 每个类都应该封装它内部信息和实现细节 而只暴露必要的方法给其他类使用 但在继承关系中 子类可以直接访问父类的属性和方法 从而造成子类和父类的严重耦合 23 05 6 3继承和组合6 3 1使用继承的注意事项 为了保证父类良好的封装性 不会被子类随意改变 设计父类通常应该遵循如下规则 尽量隐藏父类的内部数据 不要让子类可以随意访问 修改父类方法 尽量不要在父类构造器中调用将要被子类重写的方法 如果想把某些类设置成最终类 即不再派生出子类 则可以用final修饰这个类 到底何时需要从父类派生新的子类 不仅需要保证子类是一种特殊的父类 而且还需要具备以下两个条件之一 子类需要额外增加属性 而不仅仅是属性值的改变 子类需要增加自己独有的行为方式 包括增加新的方法或重写父类的方法 23 05 6 3继承和组合6 3 2利用组合实现复用 继承要表达的是一种 是 is a 的关系 而组合表达的是 有 has a 的关系 提问 试从现实生活中例举继承和组合的例子 假设有三个类 Animal Wolf和Bird 它们之间有如下图所示的继承树 23 05 6 3继承和组合6 3 2利用组合实现复用 程序清单 chapter06 test3 TestInherit java packagechapter06 test3 classAnimal privatevoidbeat System out println 心脏跳动 publicvoidbreath beat System out println 吸一口气 吐一口气 呼吸中 继承Animal 直接复用父类的breath方法classBirdextendsAnimal publicvoidfly System out println 我在天空自在的飞翔 继承Animal 直接复用父类的breath方法classWolfextendsAnimal publicvoidrun System out println 我在陆地上的快速奔跑 publicclassTestInherit publicstaticvoidmain String args Birdb newBird b breath b fly Wolfw newWolf w breath w run 23 05 6 3继承和组合6 3 2利用组合实现复用 如果仅从软件复用的角度 将上面三个类的定义改为如下形式也可实现相同的复用 程序清单 chapter06 TestComposite java 23 05 6 3继承和组合6 3 2利用组合实现复用 pac

温馨提示

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

评论

0/150

提交评论