可能好多学习编程的人想:方法的调用我还不会吗?
希望这篇博客能够给大家提供帮助
弄清楚如何在对象上应用方法调用非常重要。下面假设要调用 x.f(args,) 隐式参数 x 声明为类 C 的一个对象。下面是调用过程的详细描述:
- 编译器査看对象的声明类型和方法名。假设调用 x.f(param), 且隐式参数 x 声明为 C类的对象。需要注意的是: 有可能存在多个名字为 f, 但参数类型不一样的方法。例如,可能存在方法 f(int) 和方法 f(String)。 编译器将会一一列举所有 C 类中名为 f 的方法和其超类中访问属性为 public 且名为 f 的方法(超类的私有方法不可访问)。
至此, 编译器已获得所有可能被调用的候选方法。 - 接下来,编译器将査看调用方法时提供的参数类型。如果在所有名为 f 的方法中存在一个与提供的参数类型完全匹配, 就选择这个方法。这个过程被称为重载解析(overloadingresolution)。 例如,对于调用 x.f(“ Hello” )来说, 编译器将会挑选 f(String), 而不是 f(int)。
由于允许类型转换( int 可以转换成 double, Manager 可以转换Employee, 等等), 所以这个过程可能很复杂。 如果编译器没有找到与参数类型匹配的方法, 或者发现经过类型转换后有多个方法与之匹配, 就会报告一个错误。
至此, 编译器已获得需要调用的方法名字和参数类型。 - 如果是 private 方法、 static 方法、 final 方法或者构造器, 那么编译器将可以准确地知道应该调用哪个方法, 我们将这种调用方式称为静态绑定( static binding )。 与此对应的是,调用的方法依赖于隐式参数的实际类型, 并且在运行时实现动态绑定。在我们列举的示例中, 编译器采用动态绑定的方式生成一条调用 f(String) 的指令。
- 当程序运行,并且采用动态绑定调用方法时, 虚拟机一定调用与 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), 编译器将对隐式参数超类的方法表进行搜索。