我已就这个问题提出了几个问题,但似乎每次我得到答案时,我都会有更多问题。 这个问题是我的另一个问题的延续: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
}
据我所知,这是因为每次创建对象时,实例对象都会初始化为默认值。
如果我把初始化块说成,
System.out.print(i);
对于两个示例,在第2行的正下方,top将打印1,bottom将打印0.据我所知,初始化块在构造函数之前执行。那么这只是构造函数的概念表示吗?或者,当调用默认构造函数时,代码是否会实际更改?有人可以为我澄清一下吗?
为什么它会这样?在我的另一个问题中,似乎只会引起关于调用哪个变量的混淆。可以将实例变量声明为a = 1并在整个课程中使用吗?难道不能让它变得更简单吗?
答案 0 :(得分:1)
正如你所说,你问题中两个类之间的等价只是概念性的。
实际上,如果非静态数据字段具有初始化值,则在调用构造函数之前对其进行初始化。初始化块由编译器复制到每个构造函数的开头(在super
行之后),因此它在字段初始化之后和构造函数代码本身之前执行。
答案 1 :(得分:1)
您的示例之间的区别在于操作顺序。在您的第一个示例中,使用您所说的初始化程序块,顺序为:
a
(在声明中)a
(在初始化程序段中)...但在您的示例中,它是
a
(在声明中有效)a
(在初始化块中)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 }
答案 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将