弄清楚如何在对象上应用方法调用非常重要。下面假设要调用 x.f(args,) 隐式参数 x 声明为类 C 的一个对象。下面是调用过程的详细描述:

1 ) 编译器査看对象的声明类型和方法名。假设调用 x.f(param,) 且隐式参数 x 声明为 C类的对象。需要注意的是: 有可能存在多个名字为 f, 但参数类型不一样的方法。例如,可能存在方法 f(im) 和方法 String。) 编译器将会一一列举所有 C 类中名为 f 的方法和其超类中访问属性为 public 且名为 f 的方法(超类的私有方法不可访问)。

至此, 编译器已获得所有可能被调用的候选方法。

2 ) 接下来,编译器将査看调用方法时提供的参数类型。如果在所有名为 f 的方法中存在一个与提供的参数类型完全匹配, 就选择这个方法。这个过程被称为重载解析( overloadingresolution)。 例如,对于调用 x.f(“ Hello” )来说, 编译器将会挑选 f(String,) 而不是 f(int。)由于允许类型转换( int 可以转换成 double, Manager 可以转换成 Employee, 等等,) 所以这个过程可能很复杂。 如果编译器没有找到与参数类型匹配的方法, 或者发现经过类型转换后有多个方法与之匹配, 就会报告一个错误。

至此, 编译器已获得需要调用的方法名字和参数类型。

注释: 前面曾经说过,方法的名字和参数列表称为方法的签名。例如, f(int) 和 f(String)是两个具有相同名字, 不同签名的方法。如果在子类中定义了一个与超类签名相同的方法, 那么子类中的这个方法就覆盖了超类中的这个相同签名的方法。

不过, 返回类型不是签名的一部分, 因此, 在覆盖方法时, 一定要保证返回类型的兼容性。 允许子类将覆盖方法的返回类型定义为原返回类型的子类型。 例如, 假设Employee 类有

public Employee getBuddyO { . . . }

经理不会想找这种地位低下的员工。 为了反映这一点, 在后面的子类 Manager 中,可以按照如下所示的方式覆盖这个方法

public Manager getBuddyO { . . . } // OK to change return type

我们说, 这两个 getBuddy 方法具有可协变的返回类型。

3 ) 如果是 private 方法、 static 方法、 final 方法(有关 final 修饰符的含义将在下一节讲述)或者构造器, 那么编译器将可以准确地知道应该调用哪个方法, 我们将这种调用方式称为静态绑定(static binding ) 与此对应的是,调用的方法依赖于隐式参数的实际类型, 并且在运行时实现动态绑定。在我们列举的示例中, 编译器采用动态绑定的方式生成一条调用 f(String) 的指令。

4 ) 当程序运行,并且采用动态绑定调用方法时, 虚拟机一定调用与 x 所引用对象的实际类型最合适的那个类的方法。假设 x 的实际类型是 D,它是 C 类的子类。如果 D 类定义了方法 f(String,) 就直接调用它;否则, 将在 D 类的超类中寻找 f(String,) 以此类推。每次调用方法都要进行搜索,时间开销相当大。因此, 虚拟机预先为每个类创建了一个方法表(method table), 其中列出了所有方法的签名和实际调用的方法。这样一来,在真正调用方法的时候, 虚拟机仅查找这个表就行了。在前面的例子中, 虚拟机搜索 D 类的方法表, 以便寻找与调用 f(Sting) 相K配的方法。这个方法既有可能是 D.f(String), 也有可能是X.f(String), 这里的 X 是 D 的超类。这里需要提醒一点, 如果调用 super.f(param), 编译器将对隐式参数超类的方法表进行搜索。

现在, 查看一下程序清单 5-1 中调用 e.getSalary() 的详细过程。e 声明为 Employee 类型。Employee 类只有一个名叫 getSalary 的方法, 这个方法没有参数。 因此, 在这里不必担心重载解析的问题。

由于 getSalary 不是 private 方法、 static 方法或 final 方法,所以将采用动态绑定。虚拟机为 Employee 和 Manager 两个类生成方法表。在 Employee 的方法表中, 列出了这个类定义的所有方法:
           Employee:
           getNameO•> Employee.getNameO
           getSalaryO -> Employee.getSalaryO
           getHireDayO -> Employee.getHireDayO
           raiseSalary(double) -> Employee. raiseSal ary(doubl e)

实际上, 上面列出的方法并不完整, 稍后会看到 Employee 类有一个超类 Object,Employee 类从这个超类中还继承了许多方法,在此,我们略去了 Object 方法。

Manager 方法表稍微有些不同。其中有三个方法是继承而来的,一个方法是重新定义的,还有一个方法是新增加的。
           Manager:
           getNameO -> Employee.getNameO
           getSalaryO -> Manager.getSalary0
           getHireDayO -> Employee.getHireDayO
           raiseSalary(double) -> Employee.raiseSal ary(double)
           setBonus(double) -> Manager.setBonus(double)

在运行时, 调用 e.getSalaryO 的解析过程为:

1 ) 首先, 虚拟机提取 e 的实际类型的方法表。既可能是 Employee、 Manager 的方法表,也可能是 Employee 类的其他子类的方法表。

2 ) 接下来, 虚拟机搜索定义 getSalary 签名的类。此时, 虚拟机已经知道应该调用哪个方法。

3 ) 最后,虚拟机调用方法。动态绑定有一个非常重要的特性: 无需对现存的代码进行修改,就可以对程序进行扩展。假设增加一个新类 Executive, 并且变量 e 有可能引用这个类的对象, 我们不需要对包含调用e.getSalary() 的代码进行重新编译。 如果 e 恰好引用一个 Executive 类的对象,就会自动地调用 Executive.getSalaryO 方法。

警告: 在覆盖一个方法的时候,子类方法不能低于超类方法的可见性。特别是, 如果超类方法是 public, 子类方法一定声明为 public。经常会发生这类错误:在声明子类方法的时候, 遗漏了 public 修饰符。此时,编译器将会把它解释为试图提供更严格的访问权限

《Java 核心技术卷1 第10版》学习笔记------ -理解方法调用【重载解析、静态绑定、动态绑定】相关推荐

  1. Java 核心技术卷 II(第 8 版) – 读书笔记 – 第 1 章(下)

    22.一旦获得了一个 Charset,就可以在 Java 的 Unicode 和指定的编码格式之间进行转化,下面以 GBK 和 Unicode 之间做为例子. 从 Unicode 到 GBK: imp ...

  2. 《Java 核心技术卷1 第10版》学习笔记------异常

    异常处理的任务就是将控制权从错误产生的地方转移给能够处理这种情况的错误处理器 . 7.1.1 异常分类 在 Java 程序设计语言中, 异常对象都是派生于 Throwable 类的一个实例 . 稍后还 ...

  3. 《Java 核心技术卷1 第10版》学习笔记 ------ 泛型【进阶】

    这部分主要是结合 Java 虚拟机实现泛型的原理进一步研究如何更好的使用泛型. 8.5 泛型代码和虚拟机 虚拟机没有泛型类型对象---所有对象都属于普通类.所以编译器在编译的时候会进行类型擦除操作. ...

  4. 《Java 核心技术卷1 第10版》学习笔记------ 对象包装器、自动装箱、拆箱

    有时, 需要将 int 这样的基本类型转换为对象. 所有的基本类型都冇一个与之对应的类.例如,Integer 类对应基本类型 int.通常, 这些类称为包装器 ( wrapper ) 这些对象包装器类 ...

  5. 《Java 核心技术卷1 第10版》学习笔记------对象克隆【对象拷贝】

    由于克隆并不太常见,而且有关的细节技术性很强,你可能只是想稍做了解,等真正需要时再深人学习. 先来回忆为一个包含对象引用的变量建立副本时会发生什么 .原变量和副本都是同一个对象的引用: Employe ...

  6. 《Java 核心技术卷1 第10版》学习笔记 ------ 泛型【基础】

    泛型从Java SE 5.0 中开始出现,是 Java 程序设计语言从 1.0 版本发布以来,变化最大的部分. 使用泛型机制编写的程序代码要比那些杂乱地使用 Object 变量,然后再进行强制类型转换 ...

  7. 《Java 核心技术卷1 第10版》学习笔记------调试技巧

    调试器是 Eclipse . NetBeans 这类专业集成开发环境的一部分 . 在启动调试器之前, 本节先给出一些有价值的建议 . 1 ) 可以用下面的方法打印或记录任意变量的值 : System. ...

  8. 《Java 核心技术卷1 第10版》学习笔记------日志

    日志 API 的优点: 可以很容易地取消全部日志记录, 或者仅仅取消某个级别的日志, 而且打开和关闭这个操作也很容易 . 可以很简单地禁止日志记录的输出, 因此, 将这些日志代码留在程序中的开销很小 ...

  9. 《Java 核心技术卷1 第10版》学习笔记------ 接口(Interface)

    接口技术作用:主要描述类具有什么功能,而并不给出每个功能的是的实现. Java的继承(inheritance)是不支持的多继承的,但是Java接口是支持多继承的. 一个 Java 类实现一个接口类就必 ...

  10. 《Java 核心技术卷1 第10版》学习笔记------ Object类的 hashCode 方法

    散列码( hash code ) 是由对象导出的一个整型值.散列码是没有规律的.如果 x 和 y 是两个不同的对象, x.hashCode( ) 与 y.hashCode( ) 基本上不会相同. 在表 ...

最新文章

  1. sqlservcer行列互转
  2. 20190812_Intel Realsense D435 关于摄像头USB掉线问题问题记录汇总
  3. 在现有k8s集群中安装kubesphere时报错:metrics-server failed
  4. Spring管理Bean的生命周期
  5. 组件分页_如何创建分页组件
  6. Asp.net如何生成html静态页面
  7. python程序加密_项目源代码被好朋友“盗用”!让我痛心疾首!给Python源码加密!...
  8. 远程linux服务器中安装jupyter通过本地浏览器访问使用
  9. 使用php+gmail 发送邮件
  10. Java常见设计模式总结
  11. npm安装报错: errno ETIMEDOUT network request toXXX failed, reason: connect ETIMEDOUT
  12. 大学生数学竞赛资料目录20190403更新
  13. 几何公差基础知识之圆柱度
  14. Go语言基本语法 (下)
  15. java语言实现视频音频采集_详解js的视频和音频采集
  16. 设计模式【2.2】-- 工厂模式怎么演变成抽象工厂模式?
  17. 初步使用HTMLParser工具包【部分代码来自网络】
  18. Chatbot 架构
  19. 安卓rtmp推流app_视频直播app开发只需三步就可完成
  20. 在线详情页设计,素材免扣直接用!

热门文章

  1. 十七、去年jQuery的笔记
  2. leetcode刷题 60 61
  3. 十五、JavaScript进度条的制作
  4. CIKM 2021 | Deep Retrieval:字节跳动深度召回模型论文精读
  5. 推荐3个C++系统项目!初级开发者必学!
  6. 今晚直播 | 清华大学NLP组秦禹嘉:基于自然语言解释的数据增强
  7. 周志华《机器学习》西瓜书出全新视频课啦!
  8. 实录 | 平安人寿资深算法工程师张智:人机交互场景下的知识挖掘
  9. 01背包问题+完全背包问题+多重背包问题
  10. Python 字典中get() 函数