版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u014300008/article/details/52588331
在调用方法时,会出现静态绑定和动态绑定的概念。但是,到底是什么意思,需要仔细思考。为了弄清所有相关概念,首先来看一下函数调用过程:
编译器查看对象的声明类型和方法名。假设调用 x.f(param),并且隐式参数 x 声明为 C 类的对象。编译器会枚举出 C 类中名为 f 的方法和其超类中访问属性为 public 且名为 f 的方法。
编译器查看调用方法是提供的参数类型。如果在所有名为 f 的方法中存在一个与提供的参数类型完全匹配,就选择这个方法。这个过程叫做重载解析(overloading resolution)。
如果是 private 方法、static 方法、final 方法或者构造器,那么编译器准确知道应该调用哪个方法,我们将这种调用方式称作静态绑定(static binding)。
否则虚拟机一定调用与 x 所引用对象的实际类型最合适的那个类的方法。假设 x 的实际类型为 D,它是 C 的子类。如果 D 类定义了该方法,就直接调用它。否则将在 D 类的超类中寻找该方法,以此类推。我们将这种调用方式称作动态绑定(dynamic binding)。
Java 语言中非虚方法可以通过“静态绑定“,那么 Java 中哪些方式是非虚的呢?Java 语言规范第三版说明了哪些实例方法不是虚方法:
Java Language Specification, 3rd 写道
8.4.3.3 final Methods
A method can be declared final to prevent subclasses from overriding or hiding it. It is a compile-time error to attempt to override or hide a final method.
A private method and all methods declared immediately within a final class (§8.1.1.2) behave as if they are final, since it is impossible to override them.
It is a compile-time error for a final method to be declared abstract.
行为如同“final”的方法都无法覆写,也就无法进行子类型多态;声明为 final 或 private 的方法都被属于这类。所以除了静态方法之外,声明为 final 或者 private 的实例方法也是非虚方法。其它实例方法都是虚方法。
了解相关概念后,来看一个具体的例子:
假设有 Animal 类,它有很多子类,比如 Cat、Dog 等等。
public class Animal
{
public String shouting(int number)
{
return "Animal No."+ number + " is shouting!";
}
// other method
}
public class Cat extends Animal
{
@Override
public String shouting(int number)
{
return "Cat No."+ number + " is shouting!";
}
public String running(int number)
{
return "Cat No."+ number + " is running!";
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
然后出现这样的语句:
Animal a = new Cat();
1
这里出现了动态绑定,其中 a 声明的类型为 Animal,而运行时的类型为 Cat,所以 a 只可以访问到 Animal 中的方法和属性(如果超类 Animal 中的方法被子类 Cat 覆写的的话,那访问的就是覆写后的方法,例子中的 shouting 方法)。
如果访问子类特有的方法,就会报错。只有当 a 被强制转化为子类 Cat 时,才可以。例如:
a.running(1); // compile error if the running() method is only for Cat
((Cat)a).running(1); // work
1
2
现在看一个例子:
public class EqualsOverloadTest
{
String id;
public EqualsOverloadTest(String id)
{
this.id = id;
}
public boolean equals(EqualsOverloadTest other)
{
return (other!=null) && this.id.equals(other.id);
}
public int hashCode()
{
return id.hashCode();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
问这里的的 equals 方法会起作用吗,或者说
Set set = new HashSet();
set.add(first);
set.add(second);
System.out.println(set.size());
1
2
3
4
输出结果会是几?
答案应该是 1,也就是说 equals 方法写的是错误的。这里经常会出现问题的是混淆 method overloading(方法重载)与 method overriding(方法覆写)。
method overloading——方法名相同但参数列表不同的情况;
method overriding——参数列表与返回类型都匹配的同名方法,在派生类中覆写基类实现的情况。
所以上面的 equals 方法就是方法重载,而没有覆写 Object 中的 equals 方法,自然不起作用。
需要注意的是,
方法重载(method overloading)是 static binding 或者 compile-time binding
方法覆写(method overriding)是 dynamic binding 或者 runtime binding。
至于为什么,参见 Java 的函数重载为什么采取静态静态绑定而非动态绑定?——RednaxelaFX的回答
相关问题:
Java Dynamic Binding Confusion——Stack Overflow
Java dynamic binding and method overriding——Stack Overflow
---------------------
作者:ryanloucc
来源:CSDN
原文:https://blog.csdn.net/u014300008/article/details/52588331
版权声明:本文为博主原创文章,转载请附上博文链接!