当前位置: 首页 > 知识库问答 >
问题:

使用未初始化的最终字段 - 带/不带“this”限定符。

慕宏峻
2023-03-14

有人能解释一下为什么以下两个示例中的第一个编译,而第二个不编译?请注意,唯一的区别是第一个显式限定了对x的引用。这个,而第二个没有。在这两种情况下,在初始化之前显然尝试使用最终字段x。

我本以为这两个样本会被完全平等地对待,从而导致两者都出现编译错误。

1)

public class Foo {
    private final int x;
    private Foo() {
        int y = 2 * this.x;
        x = 5;
    }
}

2)

public class Foo {
    private final int x;
    private Foo() {
        int y = 2 * x;
        x = 5;
    }
}

共有3个答案

冯宪
2023-03-14

我认为编译器估计编写this. x意味着存在“this”,因此已调用构造函数(并且最终变量已初始化)。但是在尝试运行它时,您应该得到一个RuntimeException

呼延珂
2023-03-14
匿名用户

当您在构造函数中使用<code>this视为<code>thisx是int,它默认初始化为0。这使编译器很高兴,而且在运行时也能很好地工作。

当您不使用< code>this时,编译器在词法分析中直接使用< code>x声明,因此它会抱怨它的初始化(编译时现象)。

因此,这是的定义,它使编译器在编译中的词法分析期间将x作为对象的成员变量与直接属性进行分析,并导致不同的编译行为。

当用作主表达式时,关键字 this 表示一个值,该值是对调用实例方法的对象 (§15.12) 或正在构造的对象的引用。

唐宇定
2023-03-14

经过大量的规范阅读和思考,我得出结论:

在Java 5或Java 6编译器中,这是正确的行为。《Java语言规范的明确赋值》第三版第16章说:

每个局部变量 (§14.4) 和每个空白的最终 (§4.12.4) 字段 (§8.3.1.2) 在发生对其值的任何访问时,都必须具有明确分配的值。对其值的访问包括变量的简单名称,该名称出现在表达式中的任何位置,但作为简单赋值运算符 = 的左侧操作数除外

(重点我的)。所以在表达式< code>2 * this.x中,< code>this.x部分不被认为是“对[x的]值的访问”(因此不受明确赋值规则的约束),因为< code>this.x不是实例变量< code>x的简单名称。(注意:在上面引用的文本后面的段落中,关于何时发生明确赋值的规则确实允许类似于< code>this.x = 3的内容,并认为< code>x在其后被明确赋值;只是访问规则不算< code>this.x。)注意,根据17.5.2,在这种情况下,< code>this.x的值将为零。

在Java 7编译器中,这是一个编译器错误,但可以理解。Java 7 SE版Java语言规范的第16章“明确分配”指出:

每个局部变量(第14.4节)和每个空白最终字段(第4.12.4节,第8.3.1.2节)在对其值进行任何访问时都必须具有明确分配的值。

对其值的访问由变量的简单名称(或者,对于字段,由< code>this限定的字段的简单名称)组成,它出现在表达式中的任何位置,除了作为简单赋值运算符< code>= ( 15.26.1)的左侧操作数。

(重点我的)。所以在表达式< code>2 * this.x中,< code>this.x部分应该被认为是“对[x的]值的访问”,并且应该给出编译错误。

但是你没有问第一个是否应该编译,你问为什么它会编译(在某些编译器中)。这必然是推测性的,但我要做两个猜测:

  1. 大多数Java7编译器都是通过修改Java6编译器编写的。一些编译器编写者可能没有注意到这一变化。此外,许多Java-7编译器和IDE仍然支持Java6,一些编译器编写人员可能没有动机专门拒绝Java-7模式下他们在Java-6模式下接受的东西
  2. 新的Java7行为奇怪地不一致。类似于(false?null:this)。x仍然是允许的,因此,甚至(this)也是允许的。仍然允许x;它只是特定的标记序列thisplus加上受此更改影响的字段名。诚然,这样的不一致已经存在于赋值语句的左侧(我们可以写this.x=3,但不能写 (this))。x=3),但这更容易理解:它接受了这一点。x=3作为其他禁止施工的特殊许可情况 对象。x=3。允许这样做是有道理的。但我认为拒绝 2*这是没有意义的。x作为其他许可结构的特殊禁止情况 2*obj.x,考虑到(1)这种特殊禁止情况很容易通过添加括号来解决,(2)这种特殊的禁止情况在以前的语言版本中是允许的,而且(3)我们仍然需要特殊的规则,即 final字段在初始化之前都有默认值(例如 int0),这两种情况都是因为 ,并且由于类似 的情况。foo()其中 foo(。因此,一些编译器编写者可能没有动力进行这种不一致的更改

这两种情况都令人惊讶——我假设编译器作者对规范的每一个变化都有详细的信息,根据我的经验,Java编译器通常很好地严格遵守规范(不像某些语言,每个编译器都有自己的方言)——但是,好吧,发生了一些事情,以上是我仅有的两种猜测。

 类似资料:
  • 问题内容: 有人可以向我解释为什么以下两个示例中的第一个示例编译而第二个示例不编译吗?注意,唯一的区别是第一个用’.this’显式限定了对x的引用,而第二个则没有。在这两种情况下,显然都试图在初始化之前使用最终字段x。 我本以为两个样本将被完全平等地对待,从而导致两个样本的编译错误。 1) 2) 问题答案: 经过一堆规范的阅读和思考,我得出的结论是: 在Java 5或Java 6编译器中,这是正确

  • 问题内容: 我想知道下面的代码是否有意义,因为编译器会警告“空白的最终字段对象可能尚未初始化”。有更好的方法吗? 问题答案: 我将字段定为final,并强制构造函数将值向上传递:

  • 问题内容: 我正在用Java编程。我已经在每种方法中添加了注释,以解释它们应该做什么(根据分配)。我将我所知道的添加到了存根(这是我在研究学校提供的javadoc之后创建的)。我的问题不是几个函数,我知道testWord和setWord中有错误,但是我自己解决。我的问题是关于这条线的: 这行是由学校提供的,因此我必须假设它是正确的,我在任何地方都找不到关于常量字段值INITIAL的任何文档,因此,

  • 问题内容: 我试图简单地测试JLS保证的最终字段的初始化安全性。这是我写的论文。但是,根据我当前的代码,我无法使其失败。有人可以告诉我我做错了什么吗,或者这只是我必须一遍又一遍然后看到失败的时机而已? 这是我的代码: 我的线程正在这样调用它: 我已经多次运行过这种情况。我当前的循环产生了10,000个线程,但是我已经完成了1000、100000,甚至一百万个线程。仍然没有失败。我总是看到3和4这两

  • 问题内容: 我尝试了解通过引用同一封闭类对象初始化静态字段时初始化顺序的行为。 上面这段代码的输出是: 如果我将变量修改为除plain之外的其他任何内容: 输出为: 为什么会这样呢? 请注意,即使同时声明了两者,输出也是如此,在这种情况下,声明之前 问题答案: 静态最终成员先于其他静态成员初始化。 非最终静态成员按出现顺序初始化 因此,在您的第一种情况下: 构造函数在初始化之前首先被调用,因此被显

  • 类型参数T不应由最终的类型整数限定。不能进一步扩展Final类型 为什么使用final类型作为通配符的上限完全可以,但对类型参数抛出警告?为什么Java甚至允许通配符被最终的上层类型所约束?