1、基本概念
Java允许两种初始化代码块,一种是构造代码块,一种是静态代码块,其中构造代码块是在构造器初始化逻辑执行之前执行,而静态代码块是在类加载的时候执行。
2、构造代码块演示
下面的程序最终会输出username=5,原因是构造代码块最终会被放置到构造函数的第一行执行,构造代码块之间按照声明的顺序进行执行,可以在前向声明的代码块中对成员变量进行初始化,但是不能前向使用变量(仅能做初始化),也就意味着下面的注释代码如果打开会报错。
public class User {
{
username = "0";
// System.out.println(username);
}
{
username = "1";
}
private String username = "2";
{
username = "3";
}
{
username = "4";
}
public User() {
username = "5";
}
public String getUsername() {
return username;
}
public static void main(String[] args) {
System.out.println("username: " + new User().getUsername());
}
}
总结:
构造代码块可以在成员变量的定义之前进行声明,并且可以在这种前向声明的代码块中对成员变量进行初始化,但是不能在前向声明的构造代码块中使用变量。
构造代码块的加载顺序严格按照声明的顺序进行加载执行,构造代码块的执行将优先于构造函数的执行。
2、静态代码块
下面的程序最终会输出username=6,原因是静态代码块会随着类加载的时候被执行,调用类的静态方法或者访问类的静态属性以及实例化类的对象或者调用Class.forName()的时候都会触发类的加载,这个时候就会出发静态代码块的执行。静态代码块也会严格按照静态代码块的声明顺序进行执行。
类只会加载一次,因此整个静态代码块只会执行一次。
public class User {
static {
username = "1";
}
static {
username = "2";
}
private static String username = "3";
static {
username = "4";
}
static {
username = "5";
}
public User() {
username = "6";
}
public static String getUsername() {
return username;
}
public static void main(String[] args) {
System.out.println(User.username);
System.out.println(User.getUsername());
}
}
总结:
静态代码块会在类加载的时候触发执行,实例化类的对象,访问类的静态字段或者方法均会触发类的加载。
静态代码块的执行严格按照定义的顺序。
静态代码块也可以前向声明,前向声明的静态代码块也仅能执行初始化操作。
静态代码块仅能针对静态成员进行初始化。
3、类的初始化顺序
如下程序所示:
情况1的输出内容如下:
2
3
a=110, b=0
1
4
现在对情况1的输出原因进行解释:
- 在main方法中因为调用AppTest类的静态方法f1(),引起AppTest类的加载
- AppTest类的加载过程中会首先初始化静态成员,包括静态代码块以及静态成员的初始化
- 静态成员的初始化是按照定义顺序严格执行的
- 按照顺序首先执行静态成员t的初始化,初始化过程中需要执行new AppTest(),因此又要执行AppTest的构造方法
- 执行类的构造方法的也会引起类的加载,但是因为在调用f1的时候,类已经进行了加载,虽然还没有加载完全,此时调用构造方法不会导致第二次加载
- 执行构造方法会引起构造代码块的执行,多个构造代码块会按照定义的顺序,先于构造器中的语句执行
- 因此先执行System.out.println(“2”);
- 接着执行构造器,执行构造器的时候会给非静态成员分配空间,并且进行赋值,此时a被初始化为110,接着执行System.out.println(“3”);
- 执行System.out.println(“a=” + a + “, b=” + b);这个时候a已经被初始化了,但是b还没有来得及初始化,因为在第一次类加载的过程中,还没有执行到初始化b的语句,b被赋予默认值0。
- 因此输出2 3 a=110 b=0
- t初始化完成后,继续执行第一次类加载的过程,执行静态代码块System.out.println(“1”);
- 给b赋值112
- 然后执行f1()方法调用,因此出现上述输出结果
对情况2的解释是类似的。
public class AppTest {
public static void main(String[] args) {
// 情况1
// AppTest.f1();
// 情况2
new AppTest();
}
static AppTest t = new AppTest();
static {
System.out.println("1");
}
{
System.out.println("2");
}
AppTest() {
System.out.println("3");
System.out.println("a=" + a + ", b=" + b);
}
public static void f1() {
System.out.println("4");
}
int a = 110;
static int b = 112;
}