实例变量和构造函数

时间:2016-09-30 07:04:16

标签: java constructor instance-variables

我已就这个问题提出了几个问题,但似乎每次我得到答案时,我都会有更多问题。 这个问题是我的另一个问题的延续:Initialization in polymorphism of variables

无论如何,请考虑以下示例。

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行的正下方,top将打印1,bottom将打印0.据我所知,初始化块在构造函数之前执行。那么这只是构造函数的概念表示吗?或者,当调用默认构造函数时,代码是否会实际更改?有人可以为我澄清一下吗?

  2. 为什么它会这样?在我的另一个问题中,似乎只会引起关于调用哪个变量的混淆。可以将实例变量声明为a = 1并在整个课程中使用吗?难道不能让它变得更简单吗?

4 个答案:

答案 0 :(得分:1)

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

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

答案 1 :(得分:1)

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

  1. 将1分配给a(在声明中)
  2. 输出a(在初始化程序段中)
  3. ...但在您的示例中,它是

    1. 将0(默认值)分配给a(在声明中有效)
    2. 输出a(在初始化块中)
    3. 将1分配给a(在构造函数中)
    4. 理解实例初始化的关键是:实例初始化代码字面上被编译器复制到构造函数中 - 所有这些都包括默认值。它以源代码顺序复制,并且在构造函数中的任何内容之前(包括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查看):

      Compiled from "Example.java"
      class Example {
        Example();
          Code:
             0: aload_0
             1: invokespecial #1                  // Method java/lang/Object."":()V
             4: aload_0
             5: iconst_5
             6: putfield      #2                  // Field i:I
             9: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
            12: aload_0
            13: getfield      #2                  // Field i:I
            16: invokevirtual #4                  // Method java/io/PrintStream.println:(I)V
            19: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
            22: aload_0
            23: getfield      #2                  // Field i:I
            26: iconst_2
            27: imul
            28: invokevirtual #4                  // Method java/io/PrintStream.println:(I)V
            31: return
      
        Example(int);
          Code:
             0: aload_0
             1: invokespecial #1                  // Method java/lang/Object."":()V
             4: aload_0
             5: iconst_5
             6: putfield      #2                  // Field i:I
             9: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
            12: aload_0
            13: getfield      #2                  // Field i:I
            16: invokevirtual #4                  // Method java/io/PrintStream.println:(I)V
            19: aload_0
            20: iload_1
            21: putfield      #2                  // Field i:I
            24: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
            27: aload_0
            28: getfield      #2                  // Field i:I
            31: iconst_3
            32: imul
            33: invokevirtual #4                  // Method java/io/PrintStream.println:(I)V
            36: return
      }
      

答案 2 :(得分:1)

关于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之前的初始化块中打印零:初始化块不与字段初始化程序分开处理,它们的代码以相同的顺序混合在一起。它们出现在源代码中。

答案 3 :(得分:0)

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

您可以使用3个选项在Java中设置实例变量的值:

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. 它将进入构造函数。