java 成员变量什么时候初始化,实例探究java成员变量的初始化顺序

先来看一个例子

// initialization/InitTest.java

package initialization;

class InitTest {

private String mString = "mString1";

private static String sString = "sString1";

static {

sString = "sString2";

}

{

mString = "mString2";

}

InitTest() {

mString = "mString3";

}

}

复制代码

InitTest类中有两个变量,一个为静态变量,一个为成员变量。其中包含了几种变量初始化的方式:

定义时初始化

静态方法块对静态变量初始化

实例方法块对成员变量初始化

构造方法对成员变量初始化

当然对成员变量初始化的地方,也可以对静态变量初始化,反之则不一定可以。

那究竟上面的初始化代码是如何执行的呢?

我们首先进到initialization所在的目录,然后用javac对InitTest.java进行编译

javac initialization/InitTest.java

复制代码

此时会在initialization目录下生成InitTest.class

然后我们使用javap -c命令反编译InitTest.class

javap -c initialization/InitTest.class

复制代码

输出如下:

Compiled from "InitTest.java"

class initialization.InitTest {

initialization.InitTest();

Code:

0: aload_0

1: invokespecial #1 // Method java/lang/Object."":()V

4: aload_0

5: ldc #2 // String mString1

7: putfield #3 // Field mString:Ljava/lang/String;

10: aload_0

11: ldc #4 // String mString2

13: putfield #3 // Field mString:Ljava/lang/String;

16: ldc #5 // String sString3

18: putstatic #6 // Field sString:Ljava/lang/String;

21: aload_0

22: ldc #7 // String mString3

24: putfield #3 // Field mString:Ljava/lang/String;

27: return

static {};

Code:

0: ldc #8 // String sString1

2: putstatic #6 // Field sString:Ljava/lang/String;

5: ldc #9 // String sString2

7: putstatic #6 // Field sString:Ljava/lang/String;

10: return

}

复制代码

可以看到InitTest里有两个方法,一个是构造方法,一个是静态方法。定义初始化和方法块初始化的代码都被合并到了构造方法和静态方法块之中。

构造方法中变量初始化的顺序为:

父类构造函数

变量定义初始化

实例初始化方法块

构造函数初始化

静态方法块中的顺序为:

变量定义初始化

静态初始化方法块

现在问题来了,变量的定义和初始化方法是否可以交换顺序?交换顺序后能否编译通过?如果可以,对初始化顺序是否有影响?

比如我们将代码改成下面这样:

package initialization;

class InitTest {

static {

sString = "sString2";

}

{

mString = "mString2";

}

private String mString = "mString1";

private static String sString = "sString1";

InitTest() {

sString = "sString3";

mString = "mString3";

}

}

复制代码

重新javac编译,发现可以通过,反编译后代码如下:

Compiled from "InitTest.java"

class initialization.InitTest {

initialization.InitTest();

Code:

0: aload_0

1: invokespecial #1 // Method java/lang/Object."":()V

4: aload_0

5: ldc #2 // String mString2

7: putfield #3 // Field mString:Ljava/lang/String;

10: aload_0

11: ldc #4 // String mString1

13: putfield #3 // Field mString:Ljava/lang/String;

16: ldc #5 // String sString3

18: putstatic #6 // Field sString:Ljava/lang/String;

21: aload_0

22: ldc #7 // String mString3

24: putfield #3 // Field mString:Ljava/lang/String;

27: return

static {};

Code:

0: ldc #8 // String sString2

2: putstatic #6 // Field sString:Ljava/lang/String;

5: ldc #9 // String sString1

7: putstatic #6 // Field sString:Ljava/lang/String;

10: return

}

复制代码

我们发现初始化的顺序也变了,其中方法块中的初始化跑到了定义初始化的前面,可以这两者的顺序和其在代码中的位置保持一致。

另外我们知道静态初始化在构造方法之前调用。

从而我们可以知道,完整的初始化顺序为:

父类静态方法

子类静态方法

父类构造方法

子类构造方法

其中静态方法中包含:定义初始化和静态方法块,其顺序和代码中的位置一致。

其中构造方法中包含:(变量定义初始化和实例方法块)、构造方法初始化。定义初始化和实例方法块的顺序与代码中的位置一致。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值