Java 实例变量和构造函数

Java 实例变量和构造函数,java,constructor,instance-variables,Java,Constructor,Instance Variables,关于这个问题,我问了好几个问题,但似乎每次我得到答案,我都会有更多的问题。 这个问题是我另一个问题的继续: 无论如何,考虑下面的例子。 class A{ //1 int a = 1; //2 } 我听说这个概念上看起来像 class A { //1 int a = 0; //2 A() { //3 super(); //4 a = 1; //

关于这个问题,我问了好几个问题,但似乎每次我得到答案,我都会有更多的问题。 这个问题是我另一个问题的继续:

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

class A{       //1
   int a = 1;  //2
}              
我听说这个概念上看起来像

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

    A() {          //3
      super();     //4
      a = 1;       //5
    }
据我所知,这是因为每次创建对象时,实例对象都会初始化为其默认值

  • 如果我把它放在上面,比如说

    System.out.print(i); 
    
    在两个示例的第2行的正下方,顶部将打印1,底部将打印0。据我所知,初始化块是在构造函数之前执行的。那么,这仅仅是构造函数的概念表示吗?或者,当调用默认构造函数时,代码实际上会发生这样的更改吗?有人能帮我澄清一下吗

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


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


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

    示例之间的区别在于操作顺序。在您的第一个示例中,对于您所说的初始值设定项块,顺序是:

  • 将1分配给
    a
    (在声明中)
  • 输出
    a
    (在初始化程序块中)
  • …但在你的例子中,它是

  • 将0(默认值)分配给
    a
    (在声明中有效)
  • 输出
    a
    (在初始化块中)
  • 将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的Java8输出完全相同的字节码(在编译后使用
    javap-c示例查看):

    从“Example.java”编译而来 课例{ 示例(); 代码: 0:aload_0 1:invokespecial#1//方法java/lang/Object。“:()V 4:aload_0 5:iconst_5 6:putfield#2//字段i:i 9:getstatic#3//fieldjava/lang/System.out:Ljava/io/PrintStream; 12:aload_0 13:getfield#2//字段i:i 16:invokevirtual#4//方法java/io/PrintStream.println:(I)V 19:getstatic#3//fieldjava/lang/System.out:Ljava/io/PrintStream; 22:aload_0 23:getfield#2//字段i:i 26:iconst_2 27:imul 28:invokevirtual#4//方法java/io/PrintStream.println:(I)V 31:返回 示例(int); 代码: 0:aload_0 1:invokespecial#1//方法java/lang/Object。“:()V 4:aload_0 5:iconst_5 6:putfield#2//字段i:i 9:getstatic#3//fieldjava/lang/System.out:Ljava/io/PrintStream; 12:aload_0 13:getfield#2//字段i:i 16:invokevirtual#4//方法java/io/PrintStream.println:(I)V 19:aload_0 20:iload_1 21:putfield#2//字段i:i 24:getstatic#3//fieldjava/lang/System.out:Ljava/io/PrintStream; 27:aload_0 28:getfield#2//字段i:i 31:iconst_3 32:imul 33:invokevirtual#4//方法java/io/PrintStream.println:(I)V 36:返回 }
    您对如何将
    inta=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
        }
    }
    
    然后
    代码之前的代码块如下所示:

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

    如果未另外设置,则实例变量可立即与默认变量一起使用:对象设置为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;
        }
    }    
    
    在对象的实例化过程中,Java将

  • 如果用户没有执行,首先将变量a实例化为其默认值
  • 然后它将按照它们出现的顺序遍历任何实例初始值设定项块,最后
  • 它将进入构造函数

  • 检查提示:除了问问题。。。考虑到O
    class A{
        int a; // it is default value 0 at this point
        A() {
            a = 1;
        }
    }