Java方法覆盖和变量阴影

public class Circle { public float r = 100; public float getR() { return r; } } public class GraphicCircle extends Circle { public float r = 10; public float getR() { return r; } // Main method public static void main(String[] args) { GraphicCircle gc = new GraphicCircle(); Circle c = gc; System.out.println("Radius = " + gc.r); System.out.println("Radius = " + gc.getR()); System.out.println("Radius = " + cr); System.out.println("Radius = " + c.getR()); } } 

嗨,我无法理解上面代码的输出。 输出是:

 Radius = 10.0 Radius = 10.0 Radius = 100.0 Radius = 10.0 

我理解为什么gc.r是10.我也理解为什么gc.getR()是10(因为GraphicCircle中的getR()方法会覆盖Circle的getR()方法)。 但是我不明白为什么cr是100,而c.getR()是10(当你按照上面的代码进行类型转换为祖先类时,我很难理解inheritance中会发生什么)。

方法调用在Java中是虚拟的,这意味着无论用于访问对象的引用是什么类型,都会调用对象实际类型的方法。 另一方面,直接字段访问不是虚拟的,因此您访问它将取决于您通过它访问它的引用类型。

您可以覆盖方法,而不是字段或类变量。 所以getR()被覆盖了,它实际上显示了你所期待的。

让你访问圆的类变量而不是GC。

将类变量公开也是一个坏习惯。 它们应该是私有的或至少受到保护,并使用getter和setter访问它们( 更多关于私有字段 )。

首先,场r不是阴影 ,它是隐藏的

其次,关于覆盖方法的技术术语称为动态绑定 。 这只是意味着在运行时查找正在调用的方法时正在查询实例的实际类。

另一方面,对于字段,我们有静态绑定 。 这意味着编译器已经解析了所有字段访问,并且编译器只知道声明的类型

所以结论是:

 System.out.println("Radius = " + cr); 

这打印100.0作为变量类型为Circle ,因此编译器将对字段Circle.r的访问写入字节代码。