为什么这段代码有效?在宣布之前分配?

时间:2012-12-30 10:33:32

标签: java initialization

这是Java。我理解1 index的赋值是initialization block,它是在实例化类时首次运行的,但为什么这个有效?

public class arr {
    {
        index = 1;
    }

    int index;

    void go() {
        System.out.println(++index);
    }
    public static void main(String [] args){
          new arr().go(); 
        }
    }

输出为2。

我应该收到symbol not found编译错误。此行为是初始化块原生的吗?在正常方案int index;应该出现在index = 1;之前。

7 个答案:

答案 0 :(得分:5)

+1,看起来很奇怪。但事实上,非静态初始化块只是由javac插入到对象构造函数中。如果我们反编译arr.class,我们将获得真正的代码

public class arr {
    int index;

    public arr() {
        index = 1;
    }

    void go() {
        System.out.println(++index);
    }

    public static void main(String args[]) {
        (new arr()).go();
    }

}

让你更有趣地考虑这个难题

public class A {
    int index;

    A() {
        index = 2;
    }

    {
        index = 1;
    }
}

什么是新的A()。索引?正确的答案是2.参见反编译的A.class

class A {
    int index;

    A() {
        index = 1;
        index = 2;
    }
}

也就是说,非静态初始化块首先出现在对象构造函数

答案 1 :(得分:4)

非静态初始化块在构造函数之后运行,因此代码正确且输出符合预期。

答案 2 :(得分:4)

代码的顺序无关紧要。正如您所说,

  

在正常场景中int index;应该在index = 1之前;

这正是声明字段所发生的情况,然后分配值1。编译器不关心类中这些项的物理排序。

答案 3 :(得分:2)

Java Tutorial开始,Java编译器将初始化程序块复制到每个构造函数中。因此,这种方法可用于在多个构造函数之间共享代码块。

以下是使用final方法初始化实例变量的示例:

    class Whatever {
        private varType myVar = initializeInstanceVariable();

        protected final varType initializeInstanceVariable() {

            // initialization code goes here
        }
    }

答案 4 :(得分:1)

Java Tutorial州:

  

一个类可以有任意数量的静态初始化块,它们可以出现在类体中的任何位置。

此外,它与变量相同,您可以在声明之前“使用”:

class T {

    public static void main(String[] args) {
        T t = new T();
        System.out.println(t.a);
    }

    public int a = 14;
} 

答案 5 :(得分:1)

与某些语言(例如C)不同,类(静态)变量和实例变量的声明的顺序与Java无关。您可以在声明之前引用类/实例变量(文本)。所以即使这是有效的......

public class X {
   private int a = b;
   private int b = a;
   ...
}

......虽然初始化程序实际上没有实现任何有用的东西。

答案 6 :(得分:0)

Java是一种语言,可以在执行任何特技之前完全解析您的代码(我试图避免无聊的编译器细节)。因此,如果您的初始化程序块在字段声明之前出现并不重要,则会有特定的顺序发生。类加载(当然还有字段加载),静态初始化器,构造函数,非静态初始化器都在任何方法调用之前运行(可以在不执行构造函数和非静态成员的情况下执行静态调用)。 您应该考虑阅读一些SCJP(现在的OCPJP)书籍,如“K& B”,以真正了解幕后发生的事情。像“JVM internals”这样的教程在这个阶段可能会有些过分(但你应该考虑将它保存在你的存档中以供以后引用)。