当前位置: 首页 > 面试题库 >

为什么Java在编译时绑定变量?

从光启
2023-03-14
问题内容

考虑以下示例代码

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

方法调用将在运行时解决,并按预期调用正确的重写方法。
后来我了解到,变量访问是在编译时解决的。我期望输出为

base
derived
base
derived

因为在派生类中var阴影的重新定义是在基类中的。
为什么变量的绑定在编译时而不是在运行时发生?这是仅出于性能原因吗?


问题答案:

原因在Java语言规范的15.11节中的示例中进行了说明,如下所示:

最后一行表明,实际上,所访问的字段不依赖于所引用对象的运行时类。
即使s持有对类对象的引用T,表达式也s.x指向x类的字段S,因为表达式的类型sS。T类的对象包含两个名为的字段x,一个用于类T,一个用于其超类S

缺少对字段访问的动态查找,使得程序可以通过简单的实现高效地运行。 后期绑定和覆盖的功能是可用的,但是仅当使用实例方法时才可以使用

因此,性能是一个原因。字段访问表达式的求值规范如下:

  • 如果该字段不是static

* 如果该字段是非空白字段`final`,则结果是`T`在 _Primary_ 值所引用的对象中找到的类型为具名成员字段的值。

在您的情况下, Primary 是指derived类型为的变量MyClass

正如@Clashsoft所建议的,另一个原因是在子类中,字段没有被覆盖,而是被 隐藏了
。因此,允许根据声明的类型或使用强制类型访问哪些字段是有意义的。对于静态方法也是如此。这就是为什么根据声明的类型确定字段的原因。与实例方法重写不同,后者取决于实际类型。上面的JLS引用确实隐式提到了这个原因:

后期绑定和覆盖的功能是可用的,但仅当使用实例方法时才可用。



 类似资料:
  • 发生在运行时,因为编译器不能在决定执行哪个函数,但为什么编译器不能在编译时决定呢? 产出: 狗在吃...

  • 问题内容: 何时加载静态变量,运行时或编译时?有人可以解释一下吗? 我非常感谢您的帮助。 谢谢。 问题答案: 它们在运行时加载。 静态表示:该变量属于该类,而不属于该类的实例。因此,每个静态变量只有一个值,如果您有该类的n个实例,则没有n个值。

  • 问题内容: 当我过去用C / C ++编写库时,我习惯于拥有一种返回编译日期/时间的方法。这始终是编译到库中的,因此可以区分库的内部版本。我通过在代码中返回#define来获得此信息: C ++: 然后在编译时,我在构建脚本中有一个’-D_BuildDateTime_ = ‘。 有什么方法可以在Java中实现此目的或类似目的,而无需记住手动编辑任何文件或分发任何单独的文件。 我从同事那里得到的一个

  • 变量被编译器声明为const,导致无法编译

  • 问题内容: 如果你给 它没有编译,但是带有花括号的相同代码是: 有什么解释? 问题答案: 基本上,变量声明只能在块中声明。 查看 Java语言规范中“语句”的语法 -它包括Block,但不包括LocalVariableDeclarationStatement- 后者是block语法的一部分。 这实际上是实用主义的问题:如果没有括号,则只能使用一个语句。如果没有后续语句,则声明变量是没有意义的,因为

  • 问题内容: 在方法或类范围内,下面的行进行编译(带有警告): 在类范围中, 变量获取其默认值 ,以下给出“未定义引用”错误: 它不是第一个应该以相同的“未定义参考”错误结束吗?还是第二行应该编译?还是我缺少什么? 问题答案: tl; dr 对于 字段 ,是非法的,因为它是对的非法前向引用。您实际上可以通过编写来解决此问题,该文件可以毫无抱怨地进行编译。 对于 局部变量 ,是非法的,因为未在使用前进