问题

在方法或类范围中,下面的行编译(带警告):

int x = x = 1;

在类范围,,其中变量的默认值为,下面给出了'undefined reference'错误:

int x = x + 1;

是不是第一个x = x = 1最终会出现相同的"未定义引用"错误?或者也许第二行int x = x + 1应该编译?还是有一些我想念的东西?


#1 热门回答(101 赞)

tl;博士

Forfields,int b = b + 1是非法的,因为b是非法前向参考b。你可以通过写int b = this.b + 1来解决这个问题,编译时没有抱怨。

Forlocal变量,int d = d + 1是非法的,因为d在使用前未初始化。这是字段的而不是,它们始终是默认初始化的。

你可以通过尝试编译来查看差异
int x = (x = 1) + x;
作为字段声明和局部变量声明。前者会失败,但后者会成功,因为语义不同。

介绍

首先,字段和局部变量初始化程序的规则是非常不同的。所以这个答案将分两部分来处理这些规则。

我们将在整个过程中使用此测试程序:

public class test {
    int a = a = 1;
    int b = b + 1;
    public static void Main(String[] args) {
        int c = c = 1;
        int d = d + 1;
    }
}

b的声明无效,并且失败,其中包含3885147808错误。
声明d无效,并且因variable d might not have been initialized错误而失败。

这些错误不同的事实应该暗示错误的原因也不同。

##字段

Java中的字段初始化程序由字段初始化JLS §8.3.2管理。

字段的范围在JLS §6.3,声明范围中定义。

相关规则是:

  • 由类类型C(第8.1.6节)声明或继承的成员m的声明范围是C的整个主体,包括任何嵌套类型声明。
  • 实例变量的初始化表达式可以使用在类中声明或继承的任何静态变量的简单名称,即使其声明稍后以文本形式发生的静态变量。
  • 使用实例变量,其声明在使用后以文本形式出现有时受到限制,即使这些实例变量在范围内。有关控制实例变量的正向引用的精确规则,请参见§8.3.2.3。

§8.3.2.3说:

成员的声明只有在成员是类或接口C的实例(分别为静态)字段并且满足以下所有条件时才需要以文本方式显示:用法发生在实例中(分别为静态) C的变量初始值设定项或C的实例(分别是静态)初始值设定项。用法不在赋值的左侧。用法是通过一个简单的名称。 C是封闭用法的最内层类或接口。

除非在某些情况下,你实际上可以在声明字段之前引用字段。这些限制旨在防止类似的代码

int j = i;
int i = j;

从编译。 Java规范称"上述限制旨在捕获,在编译时,循环或其他错误的初始化。"

这些规则实际归结为什么?

简而言之,如果(a)引用位于初始化程序中,(b)引用未被赋值,则规则基本上表示你必须在引用该字段之前声明字段,(c)引用是简单的名称(没有限定符,如this.)和(d)它不是从内部类中访问的。因此,满足所有四个条件的前向引用是非法的,但是至少在一个条件上失败的前向引用是可以的。

int a = a = 1;编译,因为它违反了(b):引用a分配给,所以在a的完整声明中提及a是合法的。

int b = this.b + 1也可以编译,因为它违反了(c):referencethis.b不是一个简单的名字(它的资格是this.)。这个奇怪的构造仍然是完美定义的,因为this.b的值为零。

因此,基本上,初始化程序中对字段引用的限制阻止了int a = a + 1成功编译。

观察字段声明int b = (b = 1) + b将填写编译,因为finalb仍然是非法的前向引用。

##局部变量

本地变量声明由JLS §14.4"本地变量声明声明"控制。

局部变量的范围定义于JLS §6.3,声明范围:

  • 块(第14.4节)中局部变量声明的范围是声明出现的块的其余部分,从其自己的初始化程序开始,并在本地变量声明语句中包含右侧的任何其他声明符。

请注意,初始值设定项在声明的变量范围内。那么为什么不是int d = d + 1;编译?

原因是由于Java的规则在明确赋值(JLS §16)。确定赋值基本上表示对局部变量的每次访问必须具有对该变量的先前赋值,并且Java编译器检查循环和分支以确保在任何使用之前赋值赋值(这就是为什么明确赋值具有专用于它的整个规范部分的原因) )。基本规则是:

  • 对于本地变量或空白最终字段x的每次访问,必须在访问之前明确分配x,否则发生编译时错误。

在772263004中,对d的访问权限被解析为本地变量罚款,但由于在访问d之前尚未分配d,编译器发出错误。 Inint c = c = 1,c = 1happe首先,分配c,然后c初始化为该赋值的结果(即1)。

注意,由于明确的赋值规则,局部变量声明int d = (d = 1) + d;将成功编译(unlikethe field declarationint b = (b = 1) + b),因为d是在finald到达时明确赋值的。


#2 热门回答(86 赞)

int x = x = 1;

相当于

int x = 1;
x = x; //warning here

而在

int x = x + 1;

首先我们需要计算x+1但是x的值是未知的所以你得到一个错误(编译器知道x的值是未知的)


#3 热门回答(41 赞)

它大致相当于:

int x;
x = 1;
x = 1;

首先,int <var> = <expression>;总是相当于

int <var>;
<var> = <expression>;

在这种情况下,你的表达式为x = 1,这也是一个语句.x = 1是一个有效的语句,因为已经声明了varx。它也是一个值为1的表达式,然后将其赋值给x


原文链接