Java 派生类的意外行为

Java 派生类的意外行为,java,oop,inheritance,overriding,derived-class,Java,Oop,Inheritance,Overriding,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(); @Overri

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

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();
    }
}
输出:
Foo
酒吧
酒吧
酒吧
酒吧

输出不应该如下所示吗?
Foo
Foo
酒吧
Foo
酒吧

查看输出,似乎
str
已在派生类中被重写。但是
str
是最终的和私有的。它不可能被覆盖。有人能帮我吗

Bar中的成员str隐藏Foo中的成员str,因此如果您有Bar对象,它将始终打印“Bar”。无论变量的类型是什么,无论类型是Foo还是Bar,只要你创建了一个Bar,它就是一个Bar,不管你是将它作为Bar、Foo还是对象来对待。

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

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

一旦您实际运行了代码,
getClass()
就不在乎您是否在
Foo
类声明中编写了它。它所知道的只是对象在求值时的实际类

getClass

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


由于您的方法不是静态的,因此在构造对象时对其进行计算,而不是在声明类时。在那个时候,
Bar
就是一个
Bar
,所以为了遵守它承诺的契约,
getClass
需要返回
Bar.class
这很简单,真的:你使用
getClass()
,意思是
this.getClass()
。如果实例化
Bar
,则
getClass()
将返回class
Bar
——无论是在
Foo
中调用还是在
Bar
中调用:无论哪种方式,类都是
Bar

,获得预期输出的最简单方法就是执行以下操作

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();
示例).

为什么会被否决?好的..我刚刚用另一个值替换了getClass().getSimpleName(),它可以正常工作。你能简单解释一下getClass()是如何做到这一点的吗?@NathanHughes和resueman谢谢你的解释。是的,我做到了。那很好。我现在明白了。感谢DownVote,因为这是错误的,阴影不起作用,这是因为getClass总是返回具体的类,不管它是从Foo还是从Bar调用的。是的,我同意,特别是如果getClass().getName()只在基类中完成。但是,如果str被硬编码为“Foo”和“Bar”,那么它的行为方式仍然相同,这就是原因。第一句话仍然具有误导性,隐藏与此无关。