Java允许两种初始化代码块,一种是构造代码块,一种是静态代码块,其中构造代码块是在构造器初始化逻辑执行之前执行,而静态代码块是在类加载的时候执行。
下面的程序最终会输出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());
}
}
总结:
构造代码块可以在成员变量的定义之前进行声明,并且可以在这种前向声明的代码块中对成员变量进行初始化,但是不能在前向声明的构造代码块中使用变量。
构造代码块的加载顺序严格按照声明的顺序进行加载执行,构造代码块的执行将优先于构造函数的执行。
下面的程序最终会输出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());
}
}
总结:
静态代码块会在类加载的时候触发执行,实例化类的对象,访问类的静态字段或者方法均会触发类的加载。
静态代码块的执行严格按照定义的顺序。
静态代码块也可以前向声明,前向声明的静态代码块也仅能执行初始化操作。
静态代码块仅能针对静态成员进行初始化。
如下程序所示:
情况1的输出内容如下:
2
3
a=110, b=0
1
4
现在对情况1的输出原因进行解释:
对情况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;
}