2016-09-30 92 views
2

我已经问过关于这个主题的几个问题,但似乎每次我得到答案时,我都有更多问题。 这个问题是我的另一个问题的延续: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条,顶部将打印1和底部将打印0。据我所知,初始化块前执行构造函数。那么这仅仅是对构造函数的概念表示吗?或者,当默认构造函数被调用时,代码实际上是否会改变?有人能为我澄清这一点吗?

  2. 它为什么会这样?在我的另一个问题中,它似乎只会导致混淆哪个变量被调用。无法将实例变量声明为a = 1并在整个类中使用?这不应该让它变得更简单吗?

+0

检查http://stackoverflow.com/questions/19561332/in-what-order-do-static-blocks-and-initialization-blocks-execute-when-using-inhe – TheLostMind

+0

提示:除了询问问题。考虑一个人**真的**这样的事情应该做的事**:学习Java语言规范,例如https://docs.oracle.com/javase/specs/jls/ se8/html/jls-8.html#jls-8.3.2我在说的是:似乎你想要走到最深处。那么不要相信人们在这里给你答案(太多)。学习阅读规格(在要求其他人教你之前或之前) – GhostCat

回答

1

正如你所说,你问题中两个类之间的等价关系只是概念上的。实际上,如果一个非静态数据字段有一个初始化值,它在调用构造函数之前被初始化。初始化块由编译器复制到每个构造函数的开头(在super行之后),所以它在字段的初始化之后并在构造函数代码本身之前执行。

1

你的例子之间的区别是操作的顺序。在你的第一个例子,与初始化块,你说,顺序是:

  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观看编译后):

 
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 
} 
1

您对如何将int a = 1转换为con的说明结构是正确的,但它不是全部。

  • 如果,除了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

如果没有其他设置,实例变量可立即使用默认变量:对象被设置为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将

  1. 先初始化变量为它的默认,如果不是由用户来完成的,
  2. 然后它会经过的顺序任何实例初始化块,他们出现,最后
  3. 它将进入构造函数。