【问题标题】:Java fileds initialization orderJava 字段初始化顺序
【发布时间】:2014-12-20 17:41:54
【问题描述】:
class revi {

    static {
        i = 3;
        System.out.println("Hello World!");
    }
    static int i = 15;

    public static void main(String[] args) {
        System.out.println("Hello World!");
    }
}

上面的程序在i=3没有给出任何错误;但是当我们在静态块的println() 方法中调用i 时,它会显示错误

revi.java:6: 错误:非法前向引用 System.out.println("Hello World!"+i); ^ 1 个错误

class revi {

    static {
        i = 3;
        System.out.println("Hello World!" + i);
    }
    static int i = 15;

    public static void main(String[] args) {
        System.out.println("Hello World!");
    }
}

但是如果像这样修改上面的程序它可以工作(首先加载静态变量)println方法没有错误

class revi {

    static int i = 15;

    static {
        i = 3;
        System.out.println("Hello World!" + i);
    }

    public static void main(String[] args) {
        System.out.println("Hello World!");
    }
}

请解释内部流程...

【问题讨论】:

  • 如你所见:静态初始化块只能引用之前声明的静态变量。
  • “它显示错误”永远是不够的信息。请告诉我们您遇到了什么错误。

标签: java static


【解决方案1】:

这在8.3.3 of the JLS 部分中有介绍:

在使用后以文本形式出现的类变量的使用有时会受到限制,即使这些类变量在范围内(第 6.3 节)。具体来说,如果以下所有条件都为真,则为编译时错误:

  • 类或接口 C 中的类变量声明在使用类变量后以文本形式出现;

  • 使用是 C 的类变量初始化器或 C 的静态初始化器中的简单名称;

  • 用法不在作业的左侧;

  • C 是包含使用的最内层类或接口。

在您的情况下,当您打印该值时,所有这些 都是 true,因此会出现错误。

更改代码以移动声明后,第一个项目符号不再为真,因此可以编译。

【讨论】:

  • 你知道为什么他们认为写i = 3;不是非法的前向引用,而写System.out.println(i);是。有什么意义?
  • @pbabcdefp:老实说,我不能告诉你。我怀疑这是因为前者不需要知道 existing 值,而后者需要知道 - 它可能更容易计算出发出代码的顺序。不过不确定。
【解决方案2】:

在案例 1 中,您将使用 3 初始化变量 i,并在第 2 行使用 15。所以最终结果是变量i 持有15

在情况 2 中,您将得到 Illegal forward reference error,因为您试图在定义变量 i 之前使用它。 更新根据JLS,尚未定义的静态变量只能在赋值中使用,除非它们带有类名前缀(在这种情况下,它们会获得默认值)。例 1

static {
    i = 3;
    i = i * 2; // will give you compile error
    System.out.println("Hello World!" );
}

例如 2 在这种情况下,您正在访问以类名为前缀的变量 i。但请记住,revi.i 的值将是 0 而不是 15,因此输出将是 0

static {
    i = revi.i * 2; // this line is similar to  revi.i = revi.i * 2;
    System.out.println("Hello World!" + revi.i );
}
static int i = 15;


在情况 3 中,您首先声明并初始化 i=5,然后将 3 分配给它。所以在初始化之后i 将保持 3 作为它的值

【讨论】:

  • 你能多说一下关于第二种情况吗?为什么如果我们使用revi.i 而不是i 问题就消失了System.out.println("Hello World!" + revi.i);
  • 我更感兴趣的是“为什么允许revi.ii 不允许”,而不是“它将如何工作”。 IMO JLS 只是确认它,而不是解释。 总有一天将需要仔细观察。
【解决方案3】:

当一个java类被执行时,JVM会按顺序执行几个步骤。

从上到下识别静态成员。 从上到下执行静态变量赋值和静态块。 执行主要方法。 在这些阶段中,静态变量存在一种称为 RIWO(只读间接写入)的状态。

在 RIWO 期间,不能通过引用直接访问变量。相反,我们需要使用间接的方式来调用某些变量。

RIWO State

您可以参考该链接了解详细信息。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-31
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多