Java强制理解

时间:2016-03-15 07:44:56

标签: java types casting

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)的引用。那么为什么在这种情况下输出类型区域呢?在我的理解中,我觉得自己缺少基础。

2 个答案:

答案 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的引用,而对象midRegion的引用。编译器永远不会知道将调用哪个printMe()。它仅检查StateRegion类是否具有函数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中的继承系统。