为什么Java在编译时绑定variables?

考虑下面的示例代码

class MyClass { public String var = "base"; public void printVar() { System.out.println(var); } } class MyDerivedClass extends MyClass { public String var = "derived"; public void printVar() { System.out.println(var); } } public class Binding { public static void main(String[] args) { MyClass base = new MyClass(); MyClass derived = new MyDerivedClass(); System.out.println(base.var); System.out.println(derived.var); base.printVar(); derived.printVar(); } } 

它给出以下输出

 base base base derived 

方法调用在运行时被parsing,正如预期的那样调用正确的重写方法。
我后来得知,访问variables是在编译时parsing的。 我期待输出为

 base derived base derived 

因为在派生类中, var的重定义会影响基类中的一个。
为什么variables绑定发生在编译时,而不是在运行时? 这只是出于性能的原因?

Java语言规范中的原因在第15.11节的一个例子中进行了解释,引用如下:

最后一行显示,确实被访问的字段不依赖于引用对象的运行时类; 即使s持有对T类对象的引用,expression式sx引用类Sx字段,因为expression式s的types是S T类的对象包含两个名为x字段,一个用于T类,另一个用于其超类S

这种缺乏对字段访问的dynamic查询允许程序以直接的实现方式高效运行。 后期绑定和覆盖的力量是可用的,但只有当使用实例方法时

所以是的performance是一个原因。 如何评估字段访问expression式的规范如下:

  • 如果该字段不是static

    • 如果该字段是非空白的final ,那么结果是在由主值的引用的对象中find的typesT中的命名成员字段的值。

在你的情况下, 主要MyClasstypes为MyClass的variables。

另一个原因,就像@Clashsoft所说,在子类中,字段不会被覆盖,它们是隐藏的 。 因此,根据声明的types或使用强制types来允许哪些字段被访问是有意义的。 静态方法也是如此。 这就是为什么该字段是基于声明的types确定的。 与重写实例方法不同,它取决于实际的types。 上面的JLS报价确实提到了这个原因:

后期绑定和覆盖的function是可用的,但仅在使用实例方法时才有效。

虽然你可能对性能是正确的,但还有一个原因是为什么字段没有被dynamic调度:如果你有一个MyDerivedClass实例,你将无法访问MyClass.var字段。

一般来说,我不知道任何实际上具有dynamic可变分辨率的静态types语言。 但是,如果你确实需要它,你可以使用getter或者accessor方法(在大多数情况下应该避免使用public字段):

 class MyClass { private String var = "base"; public String getVar() // or simply 'var()' { return this.var; } } class MyDerivedClass extends MyClass { private String var = "derived"; @Override public String getVar() { return this.var; } } 

java语言的多态行为适用于方法而不是成员variables:他们在编译时devise了绑定成员variables的语言。

在java中,这是devise。 因为,要dynamic解决的字段的设置会使事情运行得慢一点。 而实际上,没有任何理由这样做。 因为,你可以使任何类中的字段都是私有的 ,并用dynamicparsing的 方法访问它们。

所以,在编译时 ,字段被parsing得更好:)