为什么重写父类object类中的toString方法后,println调用的tostring方法是重写的方法而不是object类中的toString方法?
当你在子类中重写父类 `Object` 类的 `toString` 方法后,使用 `println` 输出该子类对象时调用的是子类中重写的 `toString` 方法而不是 `Object` 类中的默认 `toString` 方法,这是由于Java中的动态方法调度机制(也称为后期绑定或运行时多态)。
### 动态方法调度
在Java中,所有非静态方法(包括实例方法)默认都是虚拟的(virtual),这意味着它们可以在运行时根据实际的对象类型来决定调用哪个版本的方法。具体来说:
- **当一个对象被创建并调用其方法时**,JVM会查找该对象的实际类型,并调用与之匹配的最具体的方法实现。
- 即使这个方法是通过指向该对象的父类类型的引用来调用的,也会执行这一规则。这是因为Java使用了**动态方法分派**的方式,在运行时根据对象的实际类型来确定调用哪个方法。
### 示例代码
考虑以下示例:
java
class Parent {
@Override
public String toString() {
return "This is Parent class";
}
}
class Child extends Parent {
@Override
public String toString() {
return "This is Child class";
}
}
public class Main {
public static void main(String[] args) {
Parent obj = new Child(); // 用父类引用指向子类对象
System.out.println(obj); // 将输出"This is Child class"
}
}
在这个例子中,尽管 `obj` 是 `Parent` 类型的引用,但它实际上指向的是 `Child` 类的一个实例。因此,当 `System.out.println(obj)` 被调用时,JVM识别到 `obj` 实际上是一个 `Child` 类的实例,并调用了 `Child` 类中重写的 `toString` 方法,而不是 `Parent` 类中的 `toString` 方法。
### 多态性
这种行为支持了面向对象编程中的一个重要概念------**多态性**。它允许基于对象的实际类型来调用适当的方法,这使得代码更加灵活和可扩展。对于像 `toString` 这样的方法,它可以让你为不同的类提供具体的字符串表示形式,从而提高代码的可读性和维护性。
综上所述,当你重写了 `Object` 类中的 `toString` 方法之后,`println` 函数调用的是子类中重写的 `toString` 方法,这是因为Java在运行时能够识别对象的真实类型,并据此选择正确的 `toString` 方法进行调用。
可是Parent类中的虚方法表,没有Child中重写的方法啊
在Java中,当你创建一个子类对象时,例如 `Child` 类继承自 `Parent` 类,并且在 `Child` 类中重写了 `toString` 方法,那么这个子类对象实际上包含了一个指向其虚方法表(vtable)的指针。这个虚方法表包含了该对象可以调用的所有实例方法的地址。
### 虚方法表的工作机制
1. **虚方法表的构建**:当一个类被加载并初始化时,JVM会为这个类生成一个虚方法表。对于每个类,这个表包含了从该类及其所有父类继承来的虚方法(即非私有、非静态的方法)。如果某个方法在子类中被重写,则虚方法表中对应条目的地址将指向子类中实现的方法;如果没有被重写,则指向父类中的实现。
2. **多态性与动态调度**:当你使用父类类型的引用来指向子类对象时,尽管引用类型是父类,但实际的对象类型决定了调用哪个版本的方法。这是因为JVM根据对象的实际类型来查找相应的虚方法表,并找到正确的实现方法。因此,即使是在父类中声明的引用变量,如果它指向的是子类对象,那么调用的方法也是子类中重写的版本。
### 示例解释
假设你有如下代码:
java
class Parent {
public String toString() {
return "This is Parent class";
}
}
class Child extends Parent {
@Override
public String toString() {
return "This is Child class";
}
}
public class Main {
public static void main(String[] args) {
Parent obj = new Child(); // 父类引用指向子类对象
System.out.println(obj); // 输出"This is Child class"
}
}
在这个例子中:
**- 当 `new Child()` 创建一个 `Child` 类型的对象时,JVM为这个对象生成了一个虚方法表,其中包含了从 `Parent` 继承的方法以及 `Child` 中定义或重写的方法。
- 如果 `Child` 类重写了 `toString` 方法,那么在 `Child` 的虚方法表中,`toString` 方法对应的条目将指向 `Child` 类中的实现。
- 在执行 `System.out.println(obj)` 时,尽管 `obj` 是 `Parent` 类型的引用,但由于它是对 `Child` 对象的引用,JVM会查询 `Child` 对象的虚方法表来找到 `toString` 方法的正确实现,并调用 `Child` 类中重写的 `toString` 方法。**
### 关键点
**- **虚方法表是基于对象的实际类型**:无论引用的声明类型是什么,JVM都会根据对象的实际类型来决定使用哪个虚方法表,从而确保调用了最具体的实现。
- **重写方法覆盖了父类方法在虚方法表中的位置**:当子类重写父类的方法时,它实际上是在子类的虚方法表中更新了该方法的实现地址,这样就能保证总是调用到最新的实现。**
综上所述,虽然 `Parent` 类的虚方法表不直接包含 `Child` 类中重写的方法,但是由于 `Child` 类有自己的虚方法表,且在运行时JVM能够根据对象的实际类型找到并调用正确的虚方法表,所以能够调用到子类中重写的 `toString` 方法。