public class Maryland extends State { Maryland() { /* null constructor */ }
public void printMe() { System.out.println("Read it."); }
public static void main(String[] args) {
Region mid = new State();
State md = new Maryland();
Object obj = new Place();
Place usa = new Region();
md.printMe();
mid.printMe();
((Place) obj).printMe();
obj = md;
((Maryland) obj).printMe();
obj = usa;
((Place) obj).printMe();
usa = md;
((Place) usa).printMe();
}
}
class State extends Region {
State() { /* null constructor */ }
public void printMe() { System.out.println("Ship it."); }
}
class Region extends Place {
Region() { /* null constructor */ }
public void printMe() { System.out.println("Box it."); }
}
class Place extends Object {
Place() { /* null constructor */ }
public void printMe() { System.out.println("Buy it."); }
}
你好。
我试图了解上述main方法的行为,该方法在运行时打印以下输出。
阅读它。 装运它。 买吧。 阅读。 把它装箱。 阅读它。
我特别难以理解main函数中最后两个printMe方法的输出。
我的理解是前两个print me操作将使用超类printMe方法,因为对象尚未明确地向下转换为子类,因此被Java编译器分别视为State和Region对象。
我也相信我理解下两个类是向下转换的输出,因此子类printMe函数将覆盖超类函数。
但是我很难理解最后两个printMe方法中发生了什么。我可以看到变量obj最初被声明为一个Object然后向下转换为一个Place被赋予对usa对象(类型为Place)的引用。那么为什么在这种情况下输出类型区域呢?在我的理解中,我觉得自己缺少基础。
答案 0 :(得分:0)
在Java中,实例方法调用遵循继承。无论引用类型是什么,它都将调用实际对象类型的方法。
引用的类型仅确定编译器知道您可以调用的方法。 e.g。
String hi = "Hello";
Object A = hi;
String hiToString = A.toString(); // calls String.toString();
int l = A.length(); // won't compile even though the String in A has a length() method.
注意:对于静态方法,实例被忽略,编译时类型决定调用哪个方法。
答案 1 :(得分:0)
在Java中,使用后期绑定调用非静态方法,这意味着我们无法确定在运行时它将调用哪个函数。
我的理解是前两个print me操作将使用超类printMe方法,因为对象尚未显式向下转换为子类,因此被Java编译器分别视为State和Region对象。
md
仍然是State
的引用,而对象mid
是Region
的引用。编译器永远不会知道将调用哪个printMe()
。它仅检查State
和Region
类是否具有函数printMe()
。md.printMe()
时,JVM将检查md
的运行时类型信息(RTTI),并知道它是State
类的对象。因此,无论声明printMe()
是什么,都会调用State
类中的md
函数。 (当然,您需要覆盖printMe()
类中的State
。如果不这样做,State
类将从其超类{{1}继承printMe()
函数}},Place
类中的printMe()
函数将被调用。您可以通过移除Place
类中的printMe()
函数来检查这一点。)根据这些规则,输出是合理的。
需要State
中的类型,因为在((Place) obj).printMe()
类中,其中没有Object
函数。但是,编译器无法确定将调用哪个函数printMe()
,它是在运行时决定的。例如,如果您将((Place) obj).printMe()
更改为((Maryland) obj).printMe();
,则输出仍然相同。
对于静态方法,这些规则不适用。您可以使用关键字“重载,覆盖和隐藏”来阅读有关它们的更多信息。这些术语将帮助您理解Java中的继承系统。