java对象初始化的执行顺序
创建:2020/4/8
修改:2020/4/10
java环境: jdk11
package muyu;
public class A {
static String sts = print("静态属性"); // 1
private String s = print("属性"); // 2
static {
print("静态初始化块");
} // 1
{
print("初始化块");
} // 2
public A() {
print("构造函数");
} // 3
static public String print(String message) {
System.out.println(message);
return message;
}
public static void main(String[] args) {
print("--------------------");
new A();
print("--------------------");
new A();
}
}
// 输出:
// 静态属性
// 静态初始化块
// --------------------
// 属性
// 初始化块
// 构造函数
// --------------------
// 属性
// 初始化块
// 构造函数
- 从上例可以看出初始化执行顺序是 静态属性,静态初始化块 -> 属性,初始化块 -> 构造函数
- 静态属性和静态初始化块可以看作是类的初始化,在类的静态属性第一次被访问,静态方法第一次被调用,或者创建第一个该类对象时进行并且只执行一次。上例静态初始化是在main方法调用时进行的。
- 属性和初始化块是对象的初始化,在创建一个对象时先于构造函数执行。
- 属性和初始化块的执行顺序取决于他们在代码中的顺序,这一点静态属性和静态初始化块也是一样
在有继承关系的情况下
添加一个和A一模一样的类B,并让A继承B
class B {
static String sts = print("B静态属性"); // 1
private String s = print("B属性"); // 2
static {
print("B静态初始化块");
} // 1
{
print("B初始化块");
} // 2
public B() {
print("B构造函数");
} // 3
static public String print(String message) {
System.out.println(message);
return message;
}
}
public class A extends B {
static String sts = print("A静态属性"); // 1
private String s = print("A属性"); // 2
static {
print("A静态初始化块");
} // 1
{
print("A初始化块");
} // 2
public A() {
super();
print("A构造函数");
} // 3
public static void main(String[] args) {
print("--------------------");
new A();
}
}
// 输出:
// B静态属性
// B静态初始化块
// A静态属性
// A静态初始化块
// --------------------
// B属性
// B初始化块
// B构造函数
// A属性
// A初始化块
// A构造函数
- 在调用A的main方法的时候,先初始化了父类B然后初始化了A,这是因为在A中可能会用到继承自B的静态属性或静态方法,在A中没有声明print方法,这个方法就继承自B。
- 在构造一个A类对象的时候,先执行B类对象的初始化再执行A类对象的初始化,也是因为我们在子类对象初始化时可能用到父类对象的属性或者方法。如果有多层继承则会从最顶层逐层向下。无论是父类对象还是子类对象其初始化过程的顺序都是 属性,初始化块 然后 构造函数。
构造函数中调用被覆盖的方法
class B {
private String s;
B() {
init();
}
protected void init() {
s = "B属性s";
print(s);
}
static public String print(String message) {
System.out.println(message);
return message;
}
}
public class A extends B {
int a = 1;
A() {
}
@Override
public void init() {
// super.init();
print(a + "");
}
public static void main(String[] args) {
new A();
}
}
//输出:
// 0
上面这个例子,B类对象在初始化时想要调用自己的init方法,但是A类覆盖了B的init方法,由于多态的关系最终调用了A的init方法。B的init方法中是要对字符串属性进行初始化的,那么在未初始化这个属性的情况下使用它,就有可能发生错误。
- 前面说过构造对象时会先执行父类对象的初始化,但在A的init方法执行的时候A的对象还没有执行初始化呢,这时我们可以看到其实在所有初始化执行前,这些属性就已经被置为默认值了。
- 最好不要在构造函数中调用非私有的非静态方法,静态方法不会被覆盖。当然如果在子类方法中不忘记调用父类的被覆盖方法,也可以达到效果。
总结
- 类的静态属性和静态初始化块,会在类静态方法第一次被调用,静态属性第一次被访问或者创建第一个该类对象时执行,并且只执行一次。在初始化一个类之前会先初始化它的父类。
- 构造一个对象时,属性,初始化块先于构造函数执行。
- 构造一个有继承关系的类的对象时,总是先执行父类对象的初始化,如果有多层继承则一直反复此规则到最顶层的类。
这篇博客是我对java学习的记录,很多分析还很片面,后期学习了会对有误或不准确的地方进行修改。