为什么超类的实例variables没有在子类方法中被覆盖

为什么超类的variables没有在子类方法中重写请参阅下面的代码..在哪个方法打印被覆盖,但variablesa不是。 以及为什么代码允许在子类中编写重复的variables..

class B { int a=10; public void print() { System.out.println("inside B super class"); } } class C extends B { int a=20; public void print() { System.out.println("inside C sub class"); } } public class A { public static void main(String[] args) { B b=new C(); b.print();//it will print inside c sub class System.out.println(ba);//it will print super class variable value=10 } } 

为什么超类的variables没有在子类方法中重写看到我的代码在下面…

因为实例variables不能在Java中被覆盖。 在Java中,只有方法可以被覆盖。

当您声明与超类中现有字段具有相同名称的字段时,新字段将隐藏现有字段。 超类的现有字段仍然存在于子类中,甚至可以使用…遵循正常的Java访问规则。

参考文献:

  • Java教程 – 隐藏字段
  • JLS 例8.3.1.1-3 – 隐藏实例字段 。

您可以参考Java语言规范中有关该主题的下列部分/示例。

  1. 例8.3.1.1-3。 隐藏实例variables
  2. 第8.4.8节。 inheritance,重写,隐藏和相关的例子

对于那些对jvm内部表面感兴趣的人来说,我的文章其实是一个额外的信息。 我们可以从检查使用javap为A类生成的字节代码开始。 下面的字节代码拆分成人类可读的基于文本的指令(助记符)。

 javap -c A.class 

在整个解体的许多细节中,我们可以把注意力集中在b.print和ba对应的行上

 9: invokevirtual #4 // Method B.print:()V ... ... 16: getfield #6 // Field Ba:I 

我们可以立即推断出用于访问方法和variables的操作码是不同的。 如果你来自C ++学校,你可能会觉得所有的方法调用在java中都是虚拟的。

现在让我们写一个与A相同的类A1,但是只需要在C中访问variables'a'。

公开课A1 {
public static void main(String [] args){
B b = new C();
b.print(); //铸造在这里是无关紧要的,因为方法无论如何都是在运行时绑定System.out.println(((C)b).a); //铸造允许我们访问C中的值
}
}

编译文件并反汇编这个类。

javap -c A1.class

你会注意到dis-assembly现在指向Ca而不是Ba

19:getfield#6 // Field Ca:I

如果你想深入挖掘,这里有更多的信息:
– invokevirtual对应于操作码0xb6
– getfield对应于操作码0xb4

您可以在http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.htmlfind有关这些操作码的全面说明的JVM规范。;
在amazon.com上查看“Java虚拟机”的书籍,可以使解码规范变得更容易一些。

我已经修改了简单的解释代码,而不是variables'a',可以说C类包含variables'c'。 其原因与C类无法访问类C本身的实例variables(没有types转换)相同。 示例如下

 class B { int a=10; public void print() { System.out.println("inside B super class"); } } class C extends B { int x=20; public void print() { System.out.println("inside C sub class"); } } public class A { public static void main(String[] args) { B b=new C(); System.out.println(bx);//will throw compile error unless b is type casted to Class C } } 

所以,在java中,编译器会通过引用而不是实例。 为了克服这个编译器使用运行时多态性 ,但它是为方法,而不是实例variables。 所以variables不能被访问没有types转换和方法,除非,重写(运行时polymoprhism),不能访问没有types转换。

所以,在我们的情况下,对于带有一个子类实例的超类的引用,在超类中查看是显而易见的。

由于实例variables不会在java中被覆盖,所以没有与它们相关的运行时多态性,因此在编译时只有通过引用来决定。

在你的代码中

 B b = new C(); b.print(); As b is of type Class B which is Parent to C and hence as there is no run time polymorphism it is decided at compile time to call instance variable of Class B. 

这是我在devise/概念层面的观点,为什么实例variables没有被覆盖。 为了简单起见,如果我们考虑抽象类,他们定义抽象方法,并期望它们被覆盖。 从来没有像抽象variables一样 。 如果有的话,那么我们可以期望通过压倒性的语言来支持它。 所以,当devise一个抽象类时,devise者为子类定义了一些常见的具体状态和常见行为(包括抽象方法)。 几乎所有的状态如果是被inheritance的(受保护的访问),那么它将被简单地inheritance,我相信在极less数情况下,其中的一些可以被重新定义,但是很less被重新声明。 所以,状态自然被期望被简单地inheritance,而行为则被期望被inheritance和覆盖。