Java"技巧",重新定义了女儿级成员

kso*_*sol 15 java inheritance

我正在接受Java考试的培训,而且在去年的课程中我遇到了一些我不理解的东西.这是代码

class Mother {
    int var = 2;

    int getVar() {
        return var;
    }
}

class Daughter extends Mother {
    int var = 1;

    int getVar() { 
        return var;
    }

    public static void main(String[] args) {
        Mother m = new Mother();
        System.out.println(m.var);
        System.out.println(m.getVar());
        m = new Daughter();
        System.out.println(m.var);
        System.out.println(m.getVar());
    }
}
Run Code Online (Sandbox Code Playgroud)

问题是"这个程序的输出是什么?".我会用2 2 1 1,但在编译和运行这段代码时,我得到2 2 2 1.

谁能解释我为什么?

谢谢阅读 !

Mat*_*Mat 16

方法调用m.getVar()是虚方法调用.第二次调用它时,它会动态调度到派生Daughter.getVar(),它会按照您的期望(访问Daugther.var并返回).

成员字段没有这种虚拟调度机制.所以m.var总是指Mother.var,即基类的那个变量的版本.

这个Daughter类可以看作有两个不同的var成员:一个来自Mother和它自己.它自己的成员"隐藏"了一个Mother,但可以通过使用从Daughter类内访问super.var.

造成这种情况的官方规格是在第8.3字段声明中的JLS.引用:

如果类声明了具有特定名称的字段,那么该字段的声明将被称为隐藏超类中具有相同名称的字段的任何和所有可访问声明,以及该类的超接口.字段声明还隐藏(§6.3.1)封闭类或接口中任何可访问字段的声明,以及任何封闭块中具有相同名称的任何局部变量,形式方法参数和异常处理程序参数.

请注意,它可以变得非常有趣(强调添加):

如果字段声明隐藏了另一个字段的声明,则这两个字段不必具有相同的类型.

和:

可能有几个路径可以从接口继承相同的字段声明.在这种情况下,该字段被认为仅被继承一次,并且可以通过其简单名称来引用而没有歧义.

这段话非常值得一读:-)


Hei*_*bug 6

专注于这些方面:

Mother m;
 m = new Daughter();
 System.out.println(m.var);
 System.out.println(m.getVar());
Run Code Online (Sandbox Code Playgroud)

你正在构建一个子对象,但你正在把它当成它的基类Mother.因此,当您访问m.var时,您正在访问基类变量var.同时,当您调用方法时,即使您引用基类引用,也会调用覆盖方法.这是方法和字段的不同行为.字段引用不能被覆盖.