当你执行A thing = new B()时,实例变量调用如何工作; 其中B是A的子类?
这可能在某处得到了回答,但我不知道该搜索什么。 想象一下,你有以下……
超类,Animal.java
public class Animal { public String noise = "squeak"; public String toString() { return noise; } }
子类,Lion.java
public class Lion extends Animal { public String noise = "ROAR!!"; public String toString() { return noise; } }
主要类Runner.java
public class Runner { public static void main(String[] args) { Animal a = new Animal(); System.out.println(a); System.out.println(a.noise); Lion b = new Lion(); System.out.println(b); System.out.println(b.noise); Animal c = new Lion(); System.out.println(c); System.out.println(c.noise); } }
输出是:
squeak squeak ROAR!! ROAR!! ROAR!! squeak
为什么c.noise会返回吱吱声? 实例方法调用和实例变量之间有什么区别,一个返回你期望的,另一个不返回? 为什么Java会这样做?
谢谢
简短回答:
您可以覆盖方法,但不能覆盖字段。
答案很长:
每个类都会看到它自己的方法和字段以及它的父节点(私有方法除外)。 如果子进程delcares一个名称相同的方法,作为其父类中方法的名称,则此方法将被覆盖 – 如果以某种方式在子实例上调用此方法(即使是从父方法之一),将使用全新的方法而不是父方法。 孩子仍然可以通过super.method(...)
调用调用他最后一个父母的原始方法。
但是当我们来到田野时,故事就不同了。 如果子元素声明了一个新字段,它的名称与父类中的字段完全相同,它将简单地隐藏父字段而不覆盖,就像本地变量隐藏全局字段一样。 因此,子方法只会看到孩子的字段,但父母的方法将继续看到父母的字段,而父母的字段将无法通过父类显示 – 这就是你所拥有的。
孩子可以通过((Parent)this).field
访问它的父母的字段。
更长的答案:
所以你真正做到这一点的方式就是定义狮子:
public class Lion extends Animal { public Lion() { noise = "ROAR!!"; } }
所以现在对于Lion实例,Animal的噪声成员变量已经更新为ROAR !!
当然,你(几乎)从来没有真正拥有类似野外的类的公共可变成员。
你不能覆盖字段, Lion
隐藏父noise
属性中的新noise
声明。 这样做:
public class Lion extends Animal { // public String noise = "ROAR!!"; // <---- Remove this line public Lion() { noise = "ROAR"; } public String toString() { return noise; } }
默认情况下,java中的所有非静态方法都是“虚函数” 。 除非它们被标记为final(这使得该方法不可覆盖)。 Java使用虚方法表来调用正确的对象的方法。
这是因为Java只讨论了方法覆盖。 成员变量只能在子类中隐藏。 所以当你说c.noise它实际上引用父类中的字符串变量作为c如果引用类型为Animal。
您感兴趣的主题是动态调度和虚拟方法表 。 基本上,通过设计,Java允许覆盖方法(假设它们是非final的),并且在运行时JVM将执行适当的实现。 这种多态属性仅提供给方法。 良好的OO设计将决定要封装的字段。