既然您已经了解了案例1、3和4,那么让我们解决案例2。
(请注意-我绝不是JVM或编译器内部工作的专家,但这是我的理解。如果阅读此书的人是JVM专家,请随时编辑此答案,以查找可能存在的任何差异)
子类中具有相同名称但签名不同的方法称为方法重载。方法重载使用静态绑定,这基本上意味着在编译时将强制“选择”(即绑定)适当的方法。编译器不了解对象的运行时类型(也称为实际类型)。所以当你写:
// Reference Type // Actual Type Sub sub = new Sub(); // Sub Sub Top top = sub; // Top Sub
编译器仅“知道” top的类型为Top(又称引用类型)。因此,当您稍后编写:
System.out.println(top.f(str)); // Prints "subobj"
编译器将“调用” top.f视为“引用”顶级类的f方法。它“知道” str的类型为String,它扩展了Object。因此,由于1)调用“
top.f”是指Top类的f方法,2)Top类中没有使用String参数的f方法,以及3)因为str是Object的子类,Top类的f方法是编译时唯一有效的选择。因此,编译器将str隐式转换为其父类型Object,因此可以将其传递给Top的f方法。(这与动态绑定相反,在动态绑定中,上述代码行的类型解析将推迟到运行时,然后由JVM而非编译器解决。)
然后在运行时,在上面的代码行中,JVM将top转换为它的实际类型sub。但是,编译器已将参数str转换为Object类型。因此,现在JVM必须在class
sub中调用f方法,该方法采用Object类型的参数。
因此,上面的代码行显示“ subobj”而不是“ sub”。
对于另一个非常相似的示例,请参见:Java动态绑定和方法覆盖
更新:在JVM的内部工作中找到了这篇详细的文章:
http://www.artima.com/underthehood/invocationP.html
我对您的代码进行了注释,以使情况更加清楚:
class Top { public String f(Object o) {return "Top";}}class Sub extends Top { public String f(String s) {return "Sub";} // Overloading = No dynamic binding public String f(Object o) {return "SubObj";} // Overriding = Dynamic binding}public class Test { public static void main(String[] args) { // Reference Type Actual Type Sub sub = new Sub(); // Sub Sub Top top = sub; // Top Sub String str = "Something"; // String String Object obj = str; // Object String // At Compile-Time: At Run-Time: // Dynamic Binding System.out.println(top.f(obj)); // Top.f (Object) --> Sub.f (Object) // Dynamic Binding System.out.println(top.f(str)); // Top.f (Object) --> Sub.f (Object) // Static Binding System.out.println(sub.f(obj)); // Sub.f (Object) Sub.f (Object) // Static Binding System.out.println(sub.f(str)); // Sub.f (String) Sub.f (String) }}


