派生类的意外行为

时间:2015-11-12 19:49:55

标签: java oop inheritance override derived-class

我有一个简单的程序如下:

class Foo {
    private final String str = getClass().getSimpleName();

    protected void print(){
        System.out.println(str);
    }
}

class Bar extends Foo {
    private final String str = getClass().getSimpleName();

    @Override
    protected void print(){
        super.print();
        System.out.println(str);
    }   
}

class FooBarMain {
    public static void main(String[] args){
        Foo foo = new Foo();
        foo.print();

        Foo foobar = new Bar();
        foobar.print();

        Bar bar = new Bar();
        bar.print();
    }
}

输出:

酒吧
酒吧
酒吧
酒吧

输出不应如下?


酒吧

酒吧

查看输出,似乎str已在派生类中被覆盖。但, str是最终的并且是私密的。它无法被覆盖。谁能帮我?

4 个答案:

答案 0 :(得分:5)

这很简单,实际上:您使用getClass()表示this.getClass()。如果您实例化Bar,那么getClass()将返回课程Bar - 无论是在Foo还是在Bar中调用:无论如何,该课程为{{ 1}}。

答案 1 :(得分:3)

并不是str被覆盖了。它只是在构造对象时计算出来的。您的Bar类有两个不同的str值,每个值都是在使用getClass().getSimpleName();初始化对象时确定的。

即使super.print()中的Bar引用str类中的Foo,该值仍然使用getClass()方法初始化Bar对象。

实际运行代码后,getClass()并不关心您是否在Foo类声明中编写了代码。它只知道对象的实际类在评估时是什么。

来自getClass

的文档
  

返回此Object的运行时类。返回的Class对象是由所表示的类的静态同步方法锁定的对象。

由于您的方法不是静态的,因此在构造对象时会对其进行求值,而不是在声明类时进行求值。在那个时间点,BarBar,因此为了遵守它所承诺的合同,getClass需要返回Bar.class

答案 2 :(得分:2)

获得预期输出的最简单方法就是

class Foo {
    private final String str = "Foo";

    protected void print(){
        System.out.println(str);
    }
}

class Bar extends Foo {
    private final String str = "Bar";

    @Override
    protected void print(){
        super.print();
        System.out.println(str);
    }   
}

getClass()没有按你的想法行事。它不返回行getClass出现的类,它返回实例的实际运行时类型。因此,如果使用new Bar()对象进行实例化,getClass()总是给您Bar.class即使代码出现在Foo中,即使编译也是如此变量的时间类型为Foo(如Foo fooBar = new Bar();示例中所示)。

答案 3 :(得分:-2)

Bar中的成员str隐藏了Foo中的成员,所以如果你有一个Bar对象,它将始终打印“Bar”。保存对象句柄的变量类型无关紧要,无论类型是Foo还是Bar,无论何时创建Bar,它都是Bar,无论您将其视为Bar,Foo还是Object。