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

为什么int p=(p=1)p;字段定义失败,但在方法中是否正常?

章振
2023-03-14
class Test{

    int p = (p=1) + p;   // ERR "Cannot reference a field before it is defined"
    int q = (q=1) + this.q; //fine!

    void f() {
        int t = (t=1) + t; // fine!
    }       
}

在第一种情况下,我理解为:何时分配(或后续添加?)执行时,p被视为未声明。

但是为什么在一种方法中它是不同的呢?OK t不会被视为未初始化,因为(t=1)是在添加之前执行的。好的,t不是一个字段,但它现在也没有声明!

我能理解吗?要不我就记住这个区别?

也许这也与此有关:

    static int x = Test.x + (x=1) + Test.x; // produces 2

    void f() {
       int y = y + (y=1) + y;  // ERR  local variable y may not have been initialized
   }

为什么是2?第一个(x=1)以某种方式计算(x未声明!!!),然后返回1,现在x已经赋值了(!?)并且包含1,所以这两个测试。x为1,但(x=1)运算符也返回1,因此结果应为1,并且作为评估测试的结果,3应(重新分配)到x中。x(x=1)试验。x表达式

部分回答:事实上,结果是具体实施的。JLS只保证二元运算符的操作数求值顺序(从左到右)。但是,如果我们有具有相同优先级的二元运算符(比如,加号),它们的求值顺序就不能得到保证。在我的例子中,加号操作符的求值顺序是最左边的,这就是为什么静态“intx=Test.x(ZERO)(x=1)Test.x(x=1之后是1”)是0 1 1(请记住,x=1是返回赋值的运算符)。在我的例子中,方法“inty=y(y=1)y最左边的加号运算符首先求值(给出错误),但如果JVM选择首先求值第二个加号运算符,则保证首先求值其左操作数,(y=1)将使y变量初始化(因此代码将编译!)

我仍然不确定为什么(x=1)不被视为未声明的字段。我依稀记得,JLS允许未声明的变量在LHS(所以任何赋值工作),但不在RHS(x, int sth=x)。我可以用下面的片段记住它:

class Test {

    { x = 7; }  // fine! Initializer is like a regular method
    int x;

    static { y = 7; }  // fine! Initializer is like a regular method
    static int y;

另外,这肯定不是Java中默认值和初始化的重复-没有直接的解释。在这里,我们不仅需要默认值(int为零)规则,还需要在一个非常复杂的组合中使用许多不同的规则(运算符优先级,特别是一些罕见的赋值特性!)。我还知道这里的赋值优先级最低,赋值是一个运算符,它返回值!

共有2个答案

柯鸿云
2023-03-14

也许我不会说得很详细,但我会尝试一下,您指出了Java中变量生命周期的非常好的例子。

int p = (p=1) + p;   // ERR "Cannot reference a field before it is defined"

在这种情况下,p是一个类字段,当编译器加载的类p尚未初始化时(类的第一次扫描,因此p尚未加载到内存中,无法计算)。

void f() {
        int t = (t=1) + t; // fine!
}

在这种情况下,编译器只加载函数的定义,而不管函数内部是什么(我的意思是,如果没有语法错误,并且没有任何类型的错误,每个IDE都可以检查这一点)。这可能是一个奇怪的声明,但很好,在调用函数并初始化t inline之前,不会对其进行计算。

static int x = Test.x + (x=1) + Test.x; // produces 2

在这种情况下x是一个静态变量,静态的东西是在类之前加载的,所以你可以想象你的编译器把x字段放在你写的上面。在这一行中,你说x等于1,所以1 1=2。这就像做这样的事

static int x = 1;
x = Test.x + Test.x;
仲涵亮
2023-03-14

阅读Java语言规范中局部变量声明的范围。您的确切问题在示例6.3-2中描述。描述是这样的:

以下程序导致编译时错误,因为局部变量p的初始化在局部变量p声明的范围内,但局部变量p还没有值,无法使用。

 类似资料:
  • 问题内容: 在以下情况中,使用以下命令在第一次调用中成功,但是在第二次调用中失败: 为什么会这样,并且有一种方法可以在方法上设置属性,使其仅存在于一个实例上,而不是针对类的每个实例? Python 3.2.2 问题答案: 简短的答案:无法将自定义属性添加到绑定方法。 长答案如下。 在Python中,有 函数对象 和 方法对象 。当定义一个类时,该语句创建一个 函数对象 ,该 对象 位于该类的名称空

  • 描述 (Description) 字符类[\p{L}&&[^\p{Lu}]]匹配除大写字母之外的任何字符。 例子 (Example) 以下示例显示了Unicode字符类匹配的用法。 package com.wenjiangs; import java.util.regex.Matcher; import java.util.regex.Pattern; public class UnicodeCh

  • 这是代码: 编译器(gcc)警告外部减法处的整数溢出: 在我的机器上获得了正确的结果1。 为什么? 这是因为指针地址是无符号整数,但ptrdiff_t是有符号整数,无法处理这些大数字吗? 我看到了 和 不要造成溢出。 我正在努力了解这里的幕后情况。这是我关于stackoverflow的第一个问题,如果我的问题可以改进,请告诉我。

  • 我正在试图理解是什么意思。即使我将其删除,该表达式的工作原理也是一样的,即: 我知道我可以用引用匹配的模式。是什么?

  • 本文向大家介绍C中的++ * p,* p ++和* ++ p之间的区别,包括了C中的++ * p,* p ++和* ++ p之间的区别的使用技巧和注意事项,需要的朋友参考一下 指针式 在C语言中,* p表示存储在指针中的值。++是前缀和后缀表达式中使用的增量运算符。*是取消引用运算符。前缀++和*的优先级相同,并且两者从右到左关联。后缀++的优先级高于前缀++和*,并且从左到右具有关联性。请参见以

  • .我的理解是,这是因为javac像解释器一样工作,逐行读取。但是当涉及到我们调用类中的字段并向其传递值的方法时,我们可以在方法声明之后定义字段。 参数eyes、tail、Teath和coat在用于将值传递给构造函数后定义。javac如何理解方法后有一个变量。允许这样做是因为我们只能定义字段,而不能在类定义中对它们进行任何操作吗?