实例变量和构造函数



我已经问了几个关于这个主题的问题,但似乎每次我得到一个答案,我就有更多的问题。这个问题是我的另一个问题的延续:变量的多态性初始化

无论如何,考虑下面的例子:

class A{       //1
   int a = 1;  //2
}              

我听说这个概念看起来像,

class A {          //1
    int a = 0;     //2
    A() {          //3
      super();     //4
      a = 1;       //5
    }

据我所知,这是因为每次创建对象时,实例对象都被初始化为其默认值。

  1. 如果我输入初始化块,比如

    System.out.print(i); 
    

    在第2行右边,对于这两个例子,顶部将打印1,底部将打印0。据我所知,初始化块在构造函数之前执行。所以这只是构造函数的概念表示吗?或者当调用默认构造函数时,代码实际上会发生变化吗?有人能给我解释一下吗?

  2. 为什么会这样?在我的另一个问题中,它似乎只会引起混淆,因为哪个变量被调用。不能实例变量只是声明a=1,并得到整个类使用?这样不就简单多了吗?

正如你所说,你的问题中两个类之间的等价只是概念上的。

实际上,如果非静态数据字段具有初始化值,则在调用构造函数之前对其进行初始化。编译器将初始化块复制到每个构造函数的开头(在super行之后),因此它在字段初始化之后和构造函数代码本身之前执行。

您对int a = 1如何转换为构造函数的描述是正确的,但这不是全部。

  • 如果,除了a,还有其他具有初始化器的实例字段,它们的所有初始化器都被收集到一个单独的块中,作为构造函数的一部分运行
  • 如果,除了字段初始化你有通用的初始化块,他们的内容被收集到同一个块,与字段初始化器。

例如,如果您有

class A {
    {
        System.out.println(a);
    }
    int a = 1;
    {
        System.out.println(a);
        System.out.println(b);
    }
    int b = 2;
    {
        System.out.println(b);
    }
    public A() {
        // Code of A
    }
}

那么Code of A之前的代码块看起来像这样:

System.out.println(a);
a = 1;
System.out.println(a);
System.out.println(b);
b = 2;
System.out.println(b);
// Code of A

现在应该清楚了,为什么在初始化块int a = 1之前的初始化块中打印0:初始化块并没有与字段初始化器分开处理,它们的代码按照它们在源代码中出现的顺序混合在一起。

您的示例之间的区别是操作的顺序。在您的第一个示例中,使用您所说的初始化器块,顺序是:

  1. 将1赋值给a(在声明中)
  2. 输出a(在初始化块)

…但是在你的例子中,它是

  1. 将0(默认值)赋值给a(在声明中有效)
  2. 输出a(在初始化块)
  3. 将1赋值给a(在构造函数中)
对我来说,理解实例初始化的关键是:实例初始化代码是从字面上复制到构造函数中。所有的,包括默认的 —由编译器。它按照源代码顺序复制,并且位于构造函数(包括super)的任何内容之前。

下面是一个更完整的例子。考虑这个类:

class Example {
    // Instance field with initializer
    private int i = 5;
    // Instance initialization block
    {
        System.out.println(this.i);
    }
    // constructor 1
    Example() {
        System.out.println(this.i * 2);
    }
    // constructor 2
    Example(int _i) {
        this.i = _i;
        System.out.println(this.i * 3);
    }
}

被编译成字节码,就像这样:

class Example {
    // Instance field
    private int i;
    // constructor 1
    Example() {
        // begin copied code
        this.i = 5;
        System.out.println(this.i);
        // end copied code
        System.out.println(i * 2);
    }
    // constructor 2
    Example(int _i) {
        // begin copied code
        this.i = 5;
        System.out.println(this.i);
        // end copied code
        this.i = _i;
        System.out.println(this.i * 3);
    }
}

在上面两种情况下,Oracle的Java 8输出完全相同的字节码(在编译后使用javap -c Example查看):

<>之前编译自"Example.java"类示例{例();代码:0: aload_01: invokespecial #1//Method java/lang/Object.":(4: aload_05: iconst_56: putfield #2//字段i: i9: getstatic #3//字段java/lang/System.out: java/io/PrintStream;12: aload_013: getfield #2//字段i: i16: invokvirtual #4//Method java/io/PrintStream.println19: getstatic #3//字段java/lang/System.out:Ljava/io/PrintStream;22: aload_023: getfield #2//字段i: i26日:iconst_227日:imuljava/io/PrintStream.println:(I31日:返回例子(int);代码:0: aload_01: invokespecial #1//Method java/lang/Object.":(4: aload_05: iconst_56: putfield #2//字段i: i9: getstatic #3//字段java/lang/System.out: java/io/PrintStream;12: aload_013: getfield #2//字段i: i16: invokvirtual #4//Method java/io/PrintStream.println19: aload_020: iload_121: putfield #2//字段i: i24: getstatic #3//字段java/lang/System.out:Ljava/io/PrintStream;27日:aload_028: getfield #2//字段i: i31日:iconst_332: imul33: invokvirtual #4//java/io/PrintStream.println36:返回}

如果没有设置,实例变量可以立即使用默认变量:对象设置为null,基本类型设置为0,false等。

在Java中有3个选项来设置实例变量的值:

1)立即声明并实例化

class A {
    int i = 1;
}

2)在实例初始化块中实例化它

class A {
    int a; // it is default value 0 at this point
    { a = 1; } //instance initializer block
} 
3)在构造函数 中实例化
class A{
    int a; // it is default value 0 at this point
    A() {
        a = 1;
    }
}    

在A对象的实例化期间,Java将

  1. 首先实例化变量a为默认值,如果用户没有这样做,
  2. 然后它会按照出现的顺序遍历任何实例初始化块,最后是
  3. 它将进入构造函数。

相关内容

  • 没有找到相关文章

最新更新