




已阅读5页,还剩74页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
Sun服务第7章 多态 目 标当完成本章后,你应该能够: 理解方法绑定 理解对象造型 使用关键字instanceof 理解并使用多态的形式参数 理解并使用多态的方法返回类型多态 句柄引用对象 方法绑定 造型 多态的形式参数句柄引用对象 没有继承之前 某类的句柄,只能引用该类的对象, 例如 Pencil pen = null; pen = new Pencil(); pen.write(“开始做家庭作业!”); 如果引用其它类的对象 则产生编译错误,不能通过编译 示例:TestPencil.java编译错误 srcTestPencil.java:11: 不兼容的类型 找到: java.lang.String 需要: cn.peter.stationery.Pencil pen = new String(Hi, how are you?); 1 错误句柄引用对象 有继承之后 某类的句柄 除了可以引用该类的对象之外 还可以引用该类的所有子类的对象 此处,子类包括直接子类和间接子类父类的句柄引用子类的对象 例如 Pencil pen = null; pen = new RubberPencil(); System.out.println(); pen.write(开始做家庭作业! 第一题的答案是 29n); pen.write(“第二题的答案是 62n”); 修改 TestPencil.java 重新编译运行原因 为什么 父类的句柄可以引用子类的对象呢? 这是由继承来保证的 父类和子类的关系 一般和特殊的关系 子类 is a 父类问题 如果用父类的句柄来引用子类的对象 那么就不能用该句柄来访问 子类有而父类没有的成员 例如 Pencil pen = null; pen = new RubberPencil(); System.out.println(); pen.write(开始做家庭作业! 第一题的答案是 29n); pen.write(第二题的答案是 62n); pen.erase(62);编译错误 srcTestPencil.java:20: 找不到符号 符号: 方法 erase(java.lang.String) 位置: 类 cn.peter.stationery.Pencil pen.erase(62); 1 错误原因 使用父类的句柄引用子类的对象就只能访问父类已经定义的成员不能访问子类已经定义但是父类没有定义的成员此处,成员包括成员变量和成员方法方法调用 某个方法在父类中已经定义 在子类中进行覆盖 通过父类的句柄引用子类的对象 使用该句柄调用该方法 是运行父类方法的方法体 还是运行子类方法的方法体呢?write() 方法 父类 Pencil 定义了该方法 子类 RubberPencil 覆盖了该方法 程序: Pencil pen = null; pen = new RubberPencil(); System.out.println(); pen.write(开始做家庭作业! 第一题的答案是 29n); pen.write(“第二题的答案是 62n”);类图运行结果 运行的是子类中方法 write(String) 的方法体 看来还是有一定智能性的 :)小结 句柄引用对象 如果没有继承 某类的句柄只能引用该类的对象 有继承 某类的句柄可以引用该类的对象 还可以引用该类子类的对象 子类包括直接子类和间接子类访问成员 通过父类的句柄引用子类的对象 则不能访问父类没有定义而子类定义的成员 如果父类中的方法在子类中进行了覆盖 通过父类的句柄引用子类的对象 通过该句柄调用该方法 则运行子类中方法的方法体方法调用 比较智能 虽然使用的是父类的句柄 运行的仍然是子类方法的方法体 需要把方法的调用与方法体进行关联 绑定,binding方法绑定 把方法的调用和方法的方法体相关联的过程 由编译器来实现 例如, Pencil pen = new RubberPencil(); pen.write(“开始做家庭作业! 第一题的答案是 29n”); 通过 pen 来调用 write() 方法 可以运行类 Pencil 的 write() 方法 也可以运行类 RubberPencil 的 write() 方法 具体运行哪一个方法的方法体由编译器决定方法绑定 示意图方法绑定的形式 方法的绑定有两种形式: (1) 编译时刻绑定 compile-time binding (2) 运行时刻绑定 runtime binding编译时刻绑定 由于编译时刻早于运行时刻 因此也称为 早期绑定 绑定方式: 根据该句柄的类型 而不是该句柄实际引用对象的类型 来进行方法的绑定编译时刻绑定 如果按照该方式进行绑定 那么以下程序 Pencil pen = new RubberPencil(); pen.write(“开始做家庭作业! 第一题的答案是 29n”); pen.write() 中 pen 句柄的类型是类 Pencil 因此应调用类 Pencil 的 write() 方法的方法体 但是运行结果并非如此 所以该方法的绑定未采用编译时刻绑定形式编译时刻绑定的特点 如果绑定能在编译时刻完成 那么程序在运行时可以直接调用相应方法体 因此程序的性能较高 但同时 该绑定形式会损失一定的灵活性运行时刻绑定 把绑定从编译时刻推迟到了运行时刻 程序在运行的时候再进行方法的绑定 也称为 后期绑定 无疑会降低程序的性能 但同时会获得灵活性 例如,选举 总统大选,民意调查 奥运会举办城市扔钢崩儿 小明有两支笔,一支是普通铅笔 另一支是橡皮铅笔 他扔钢崩儿来决定使用哪一支笔写作业 如果是正面就使用普通铅笔 如果是反面就使用橡皮铅笔 示例:Coin.java PencilSelector.java扔钢崩儿 看天用笔Pencil pen = null;if (Coin.throwOut() = Coin.ZHENG_MIAN) pen = new Pencil();else pen = new RubberPencil();pen.write( 开始做家庭作业! n);先声明一个 Pencil 类的句柄 pen 如果是正面则 pen 引用 Pencil 类的新建对象 否则引用 RubberPencil 类的新建对象小问题:如果钢崩儿立起来怎么办?运行时刻 在扔钢崩儿之前 不知道会是哪一面 因此也不知道会用普通铅笔还是橡皮铅笔 只有等到扔完后才知道 这就是 运行时刻的含义 程序在编译的时刻无法作出准确的判断 因此只好推迟到运行时刻再决定 这样会损失一些性能 但是带来了很大的灵活性动态绑定 运行时刻绑定 后期绑定 用于能够根据对象的确切类型 来进行绑定 因此也称 动态绑定 编译时刻绑定 早期绑定 静态绑定另一种写法if (Coin.throwOut() = Coin.ZHENG_MIAN) Pencil pen = new Pencil();else RubberPencil pen = new RubberPencil();pen.write( 开始做家庭作业! n); 以上程序 能通过编译吗?编译错误 srcPencilSelector.java:38: 找不到符号 符号: 变量 pen 位置: 类 PencilSelector pen.write( 开始做家庭作业! n); 1 错误原因分析Pencil pen = null;if (Coin.throwOut() = Coin.ZHENG_MIAN) pen = new Pencil();else pen = new RubberPencil();if (Coin.throwOut() = Coin.ZHENG_MIAN) Pencil pen = new Pencil();else RubberPencil pen = new RubberPencil();分析局部变量 pen 的作用域 成员变量的访问 成员变量的访问 声明了某类的句柄 并通过该句柄来访问成员变量 那么要求 该类必须拥有该成员变量 如果没有,则产生编译错误 错误信息:找不到符号 错误位置指向该句柄和成员变量之间的点号 .成员方法的调用 成员变量的访问 声明了某类的句柄 并通过该句柄来调用成员方法 那么要求 该类必须拥有该成员方法 如果没有,则产生编译错误 错误信息:找不到符号 错误位置指向该句柄和成员方法之间的点号 .编译时刻 通过句柄声明的类型 来检查 通过该句柄访问的成员变量和成员方法是否存在 如果不存在,则产生编译错误 同时可以进行一些方法的绑定 静态方法 private 方法 final 方法编译时刻 因此 使用父类的句柄引用子类的对象 就只能访问父类已经定义的成员 不能访问子类已经定义 但是父类没有定义的成员 此处,成员包括成员变量和成员方法运行时刻 主要进行一些方法的绑定 如 非静态、非 private、非 final 的方法 以上修饰符之间是并且的关系 在 Java 语言中 上述方法全部都在运行时刻进行绑定 这是造成 Java 速度比 C+ 慢的原因之一找回子类的成员 如果使用父类的句柄来引用子类的对象 则只能访问父类的成员 不能访问子类定义而父类未定义的成员 但是,这确实是一个子类的对象 那么 如何才能找回哪些子类定义的成员呢? 解决办法: 造型,cast造型 在两种类型之间做转换 对基本数据类型而言这是很常见的 转换分为两种: 自动类型转换 强制类型转换自动类型转换 基本数据类型中的数值类型 byte / short / int / long / float / double 从数据表示范围窄的 转换到数据表示范围宽的 可以由编译器自动转换 以上顺序中从左到右可以进行自动转换 从右到左则不能进行自动转换 必须使用强制类型转换,否则产生编译错误编译错误 示例:TestConversion.java srcTestConversion.java:15: 可能损失精度 找到: float 需要: long lg = f; 1 错误强制类型转换 例如 long lg; float f = 3.14f; / 注意,此处必须加上 f,否则产生编译错误 / 因为任何带小数点的数值默认都是 double 类型 lg = (long) f; int i, j; double r; i = 29; j = 6; r = i / j; r = (double) i) / j;造型 造型就是把对象从一个类转换为另一个类 此处,类可以是具体类、抽象类、接口 例如 Pencil pen = new RubberPencil(); pen.write(“开始做作业!”); 以上是把对象 pen 从类 RubberPencil 转换为另一个类 Pencil上溯造型 把子类的对象造型为父类的对象,upcast 上溯造型是安全的 不会出现类型不匹配的情况 是由编译器自动执行的 向上造型 向上 画类图时,父类在上,子类在下类图下溯造型 把父类句柄引用的对象 造型为子类的对象 例如 定义类 PencilFactory 该类的静态成员方法 getPencil() 返回一个 Pencil 类的对象 产生该对象的方法还是 扔钢崩儿 示例:PencilSelectorWithFactory.java工厂 工厂,factory 用于生产对象 对象的提供者 向对象的使用者生产、提供对象 工厂方法的返回类型 在面向对象程序设计中使用很普遍 包括 Java / C+ / C# 等下溯造型 下溯造型必须由程序员强制进行 例如,在类 PencilSelectorWithFactory 的 main() 方法中 可以进行下溯造型: Pencil pen = null; pen = PencilFactory.getPencil(); System.out.println(); pen.write( 开始做家庭作业! n); RubberPencil rp = null; rp = (RubberPencil) pen; 重新编译程序运行结果 由于是随机获得的 Pencil 类的对象 有可能是 RubberPencil 类的对象 也可能不是 RubberPencil 类的对象运行结果 M:Javajava PencilSelector使用橡皮铅笔书写: 开始做家庭作业!使用橡皮铅笔书写:第一题的答案是 36 M:Javajava PencilSelector使用铅笔书写: 开始做家庭作业!Exception in thread main java.lang.ClassCastException: cn.peter.stationery.Pencil at PencilSelector.main(PencilSelector.java:49)原因分析 如果从 PencilFactory 获得的是 RubberPencil 类的对象 则程序运行一切正常 如果获得的不是 RubberPencil 类的对象 则在运行时刻产生了不正常的情况,异常 强行把一个 Pencil 类的对象 造型为 RubberPencil 类的对象 造型失败,产生 类造型异常 java.lang.ClassCastException如何知道? 由于是在运行时刻动态产生的对象 因此在编译时刻无法提前知晓 那程序应该如何判断 获得的 Pencil 对象是不是 RubberPencil 类的对象呢? 使用 Java 语言的关键字 instanceofinstanceof Java 语言的关键字 用来判断某个对象是否是指定类的对象 结果是 boolean 类型,true / false 一般出现在 if 的条件语句中 语法: 句柄 instanceof 类 例如,判断 pen 是否是 RubberPencil 类的对象 pen instanceof RubberPencil使用 instanceof 进行判断 首先使用 instanceof 进行判断 如果判断成立再使用下溯造型 修改 PencilSelectorWithFactory 的 main() 方法Pencil pen = null;pen = PencilFactory.getPencil();System.out.println();pen.write( 开始做家庭作业! n);RubberPencil rp = null;if (pen instanceof RubberPencil) rp = (RubberPencil) pen; rp.write( 第一题的答案是 36n); rp.erase( 第一题的答案是 36n);instanceof 即使是在运行时刻动态产生的对象 使用 instanceof 也能进行相应的判断 并获得正确的结果RubberPencil 该类的对象 是一个 RubberPencil 同时,也是一个 Pencil 还是一个 Eraser 一个对象,多种形态 用多个类进行 instanceof 判断 判断结果都成立 示例:TestPoly.java多种形态 以下运行的结果均为 true RubberPencil rp = new RubberPencil(); boolean b; b = rp instanceof RubberPencil; b = rp instanceof Pencil; b = rp instanceof Eraser; b = rp instanceof Object; 表明 rp 在不同场合可以作为一个 RubberPencil 或 Pencil 或 Eraser 或 Object 拥有了这四个类的所有成员instanceof instanceof 后面可以跟 非抽象类、抽象类、接口 如果是非抽象类 则判断该对象是否是该类或其子类的对象 如果是抽象类 则判断该对象是否是该抽象类子类的对象 如果是接口 则判断该对象是否是实现了该接口的类的对象 以上子类均包括直接子类和间接子类其它造型 如果类 A 与类 B 没有任何关系 然后把类 A 的对象造型为类 B 的对象 会发生什么情况呢? 例如,判断对象 rp 是否是 String 类的对象 修改 TestPoly.java 重新编译编译错误 srcTestPoly.java:18: 不可转换的类型 找到: cn.peter.stationery.RubberPencil 需要: java.lang.String + (rp instanceof String); 1 错误分析 由于在编译时刻即可判断出 rp 不是 String 类的对象,因此该程序产生了一个编译错误 不能通过编译 Java 编译器的智能性原则 凡是能在编译时刻解决的问题 就决不推迟到运行时刻解决 这样可以提高程序的性能 方法绑定除外 因为这个问题在编译时刻无法全部解决多态的形式参数 该形式参数可以是 基本数据类型 引用类型形式参数: 基本数据类型 求平方根 public static double sqrt(double a); 形式参数是一个 double 类型的变量 则实际参数则可以是 byte / short / int / long / float / double 不需要针对上述每一种类型 定义一个求平方根的方法 因此上述方法相当于定义了 6 个方法多个方法 相当于定义了以下 6 个方法 public static double sqrt(byte a); public static double sqrt(short a); public static double sqrt(int a); public static double sqrt(long a); public static double sqrt(float a); public static double sqrt(double a); 那么为什么定义一个方法 就可以满足要求了呢?参数类型 在调用 Math.sqrt(double) 方法时 实际参数可以是 byte / short / int / long / float / double 这六种类型中 前五种由编译器均可自动转换为 double 类型 因此 看上去实参和形参类型不是完全相同 但是通过自动类型转换可以解决该问题 实参和形参依然是匹配的,type match形式参数: 引用类型 如果某方法的形式参数是引用类型 即一个句柄 例如,是类 A 的句柄 那么,对于实际参数有什么要求呢 能够使得实参和形参是匹配的? 实际参数 类 A 或其直接子类或间接子类的对象小学生 类小学生 pupil 示例:Pupil.java 成员方法: public void doHomework(Pencil pen) pen.write( 开始做家庭作业!n); pen.write( 第一题的答案是 29n); if (pen instanceof RubberPencil) RubberPencil rp = (RubberPencil) pen; rp.erase( 第一题的答案是 29n); 形参与实参 形参 Pencil 类的句柄 pen 实参 Pencil 类的句柄 pen 该对象通过 PencilFactory 类的 getPencil() 方法获得 可能是 Pencil 类的对象 可能是 RubberPencil 类的对象 根据前述讨论,形参和实参是匹配的形参: Object 如果形参是 Object 则相应的实参可以是任何类的对象 此一个方法就相当于定义了 n 多个方法 n 类的个数 类 Object 有一个成员方法 public boolean equals(Object obj); 该方法的形参就是 Object 这样可以把当前对象和任何类的对象进行比较抽象类的句柄 抽象类的句柄 抽象类不能实例化 但可以声明抽象类的句柄 例如,抽象类 Shape 示例:T
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 教师招聘之《小学教师招聘》考试押题卷附参考答案详解(基础题)
- 2025年教师招聘之《幼儿教师招聘》综合提升测试卷附参考答案详解(研优卷)
- 教师招聘之《幼儿教师招聘》通关检测卷附答案详解【基础题】
- 教师招聘之《小学教师招聘》能力提升打印大全审定版附答案详解
- 2025年智能建筑系统集成节能降耗中的建筑能耗管理平台构建报告
- 2025年环境监测智能化技术发展趋势与数据质量控制策略分析报告
- 2023年广东省华南师大附中平行班中考一模数学试题及答案
- 教师招聘之《小学教师招聘》考前冲刺练习题完美版附答案详解
- 内蒙古呼伦贝尔农垦集团有限公司招聘笔试题库及答案详解(夺冠系列)
- 2025年教师招聘之《幼儿教师招聘》经典例题及一套答案详解
- 场景速写课件讲解
- 2025广东惠州惠城区招聘社区工作站工作人员66人笔试备考题库及答案解析
- 第15课 红红火火中国年(教学课件)小学二年级上册 统编版《道德与法治》新教材
- (2025秋新版)教科版三年级上册科学全册教案
- 2025年新西师大版数学三年级上册全册课件
- 食品安全总监、食品安全员考核考试测试题及答案
- 第8课 西溪湿地教学设计-2025-2026学年小学地方、校本课程浙教版(2021)人·自然·社会
- 江淮十校2026届高三第一次联考物理试卷(含答案解析)
- 网络货运行业知识培训课件
- 人体十二经络系统解析
- 1.8《天气的影响》教学设计-教科版三上科学(新教材)
评论
0/150
提交评论