JAVA学习脚印6: java方法调用机制

JAVA学习脚印6: java方法调用机制

本节学习java方法调用的机制,内容参考自《java核心技术》这里并没有深入到虚拟机部分的内容。

1.静态绑定与动态绑定(重点内容)

 

1)方法调用的过程——三步走,开销过大

Step1: 根据对象声明类型和方法名,确定候选函数集合(Sarch candidate methods)。

假设调用x.f(param),且隐式参数x的类型为C类的对象,则编译器会一一列举所有C类中名为f的方法和其超类中访问属性为public且名为f的方法。

Step2: 根据方法调用的参数类型,进行重载解析,定位目的函数(Fnd target method)。

这个过程中,如果在所有名为f的方法中存在一个与提供的参数类型完全匹配,就选择这个方法。例如调用x.f("hello")来说,编译器会挑选x.f(String),而不是x.f(int)。但是重载解析的过程中允许类型转换(例如int转double),重载过程如果出现二义性即找到多个与之匹配的方法调用或者没有与之匹配的调用,则出错。

补充:覆盖方法的要求

子类覆盖父类的方法,那么需要在子类中定义一个与父类相同签名(方法的名字和参数列表称为方法的签名)的方法。java新版本中允许在覆盖父类的方法时,不要求覆盖方法时方法的返回类型一定要与父类相同,而允许子类覆盖父类方法时返回类型为原返回类型的子类型。例如父类返回Employee类型,子类可以返回Manager类型。

Step3: 确定调用是静态绑定还是动态绑定,然后调用方法(Get binding type and call it)。

静态绑定:在方法调用的过程中,对于private、static 、final方法或者构造器方法,编译器可以准确地知道要调用哪个方法,我们称这种称这调用方式为静态绑定(static binding)。

动态绑定: 与静态绑定对应的是动态绑定。当调用的方法依赖于隐式参数的实际类型,并且在运行时根据引用对象类型决定具体调用哪个类的方法的调用方法称为动态绑定(dynamic binding)。

当采用动态绑定调用方式时,假设调用x.f(String),如果子类定义了该方法就直接调用它,否则在上层的超类中查找该方法,直至找到该方法则调用它或者没找到该方法而报错。这采用的是在继承层次上的一种就近原则

为了便于记忆,我把这个过程是没有利用方法表的方法调用步走:

Search candidate methods ---> Find target method---> Get binding type and call it


2)方法表(method table)——减少开销,实际中的运用


为了减少每次方法调用的时间开销,虚拟机预先为每个类建立了一个方法表,其中列出了所有方法的签名和实际调用的方法。这是对上面四步走方法的改进,在真正调用方法的时候,虚拟机查这个表就行了。以Employe类和Manager类的方法举例:


JAVA学习脚印6: java方法调用机制_第1张图片

在例5-1中,item.getSalary()方法的调用过程如下:

首先,虚拟机提取item的实际类型的方法表;接下来,虚拟机搜索定义getSalary签名的类,此时虚拟机已经知道该调用哪个方法了;最后虚拟机调用方法。这就是利用了方法表的三步走过程:

process: Get method tabel-->Get the right class--->Call the target method .

动态绑定是多态特性的一个重要应用,它无需对现有代码进行修改,即可实现程序的拓展。例如新增加一个销售员类,在执行item.getSalary()方法时将无需修改代码,十分方便。


Update:关于覆盖和重载的疑问

1)覆盖和重载的区别,摘自stackOverflow

Method overriding is when a child class redefines the same method as a parent class, with the same parameters. For example, the standard Java class java.util.LinkedHashSet extends java.util.HashSet. The method add() is overridden in LinkedHashSet. If you have a variable that is of type HashSet, and you call its add() method, it will call the appropriate implementation of add(), based on whether it is a HashSet or a LinkedHashSet. This is called polymorphism.

Method overloading is defining several methods in the same class, that accept different numbers and types of parameters. In this case, the actual method called is decided at compile-time, based on the number and types of arguments. For instance, the method System.out.println() is overloaded, so that you can pass ints as well as Strings, and it will call a different version of the method.

2)子类覆盖父类方法的返回类型兼容性问题 摘自stackOverflow

An overridden method may have a more specific return type. That is, as long as the new return type is assignable to the return type of the method you are overriding, it's allowed.

For example:

class ShapeBuilder {
    ...
    public Shape build() {
    ....
}

class CircleBuilder extends ShapeBuilder{
    ...
    @Override
    public Circle build() {
    ....
}

This is specified in section 8.4.5 of the Java Language Specification:

Return types may vary among methods that override each other if the return types are reference types. The notion of return-type-substitutability supports covariant returns, that is, the specialization of the return type to a subtype.

A method declaration d1 with return type R1 is return-type-substitutable for another method d2 with return type R2, if and only if the following conditions hold:

  • If R1 is void then R2 is void.

  • If R1 is a primitive type, then R2 is identical to R1.

  • If R1 is a reference type then:

    • R1 is either a subtype of R2 or R1 can be converted to a subtype of R2 by unchecked conversion (§5.1.9), or

    • R1 = |R2|

你可能感兴趣的:(JAVA学习脚印6: java方法调用机制)