时间:2023-04-14
标签: #Java #多态 #实现机制 #示例
一旦学会了多态,就可以以多态的思维方式考虑每件事。然而,只有普通的方法调用可以是多态的。例如,如果你直接访问一个属性,该访问会在编译时解析:
// polymorphism/FieldAccess.java
// Direct field access is determined at compile time
class Super {
public int field = 0;
public int getField() {
return field;
}
}
class Sub extends Super {
public int field = 1;
@Override public int getField() {
return field;
}
public int getSuperField() {
return super.field;
}
}
public class FieldAccess {
public static void main(String[] args) {
Super sup = new Sub();
// Upcast
System.out.println("sup.field = " + sup.field + ", sup.getField() = " + sup.getField());
Sub sub = new Sub();
System.out.println("sub.field = " + sub.field + ", sub.getField() = " + sub.getField() + ", sub.getSuperField() = " + sub.getSuperField())
}
}
//输出: sup.field = 0, sup.getField() = 1 sub.field = 1, sub.getField() = 1, sub.getSuperField() = 0
我注意到上述的代码中 sup.field = 0, sup.getField() = 1,这样我对于 Java 底层实现多态的机制有些迷惑,他是如何确定到底是哪个方法要被执行的呢,是否是有个存储方法的栈之类的呢?
在 Java 中,多态仅适用于方法调用,而不适用于字段访问。这就是为什么在上面的例子中,sup.field
的值为0,而 sup.getField()
的值为1。字段的访问在编译时就已经确定,而方法调用则在运行时动态绑定。
在这个例子中,当我们执行以下语句:
Super sup = new Sub(); // Upcast
sup
是一个 Super
类型的引用,但它实际上引用了一个 Sub
类型的对象。当我们访问字段 sup.field
时,编译器在编译时就已经确定我们访问的是 Super
类型的 field
,因此返回值为0。
然而,当我们调用 sup.getField()
时,JVM会在运行时检查sup
引用的实际对象类型(即 Sub
类型),并调用 Sub
类中的 getField()
方法。这就是为什么 sup.getField()
返回1的原因。
多态背后的原理是利用了一种称为“虚方法表”(vtable)的机制。简而言之,当一个类被加载时,JVM会为其创建一个包含其所有方法的表。对于继承自父类的方法,子类中若有重写的方法,虚方法表中的对应条目将被子类方法替换。当我们通过一个引用调用一个方法时,JVM会根据引用的实际对象类型查找对应的虚方法表,然后调用表中对应的方法。
这就是Java实现多态的基本机制。要注意的是,多态仅适用于方法调用,不适用于字段访问。因此,在处理多态时,需要确保只通过方法调用来实现多态行为。