我已经问了几个关于这个主题的问题,但似乎每次我得到一个答案,我就有更多的问题。这个问题是我的另一个问题的延续:变量的多态性初始化
无论如何,考虑下面的例子:
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
行之后),因此它在字段初始化之后和构造函数代码本身之前执行。
您对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赋值给
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的Java 8输出完全相同的字节码(在编译后使用javap -c Example
查看):
如果没有设置,实例变量可以立即使用默认变量:对象设置为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将
- 首先实例化变量a为默认值,如果用户没有这样做,
- 然后它会按照出现的顺序遍历任何实例初始化块,最后是
- 它将进入构造函数。