提前总结:
执行顺序为:
- 执行静态代码块
- 执行构造代码块
- 执行构造函数
在继承中的执行顺序为:
- 执行父类的静态代码块
- 执行子类的静态代码块
- 执行父类的构造代码块
- 执行父类的构造函数
- 执行子类的构造代码块
- 执行子类的构造函数。
注意:如果类中存在静态初始化或者静态代码块,在JVM中只会被加载一次,即使后面再次出现该类的实例也不会再加载。
那么为什么会出现这种情况呢?
- 当我们在创建子类时,实际上子类的构造方法的第一行存在一个隐式的super,super是一个指向父类的指针,所以在执行构造方法时会通过super来指向父类同时会执行父类的构造方法。
说完静态代码块的执行顺序,就到了构造代码块和构造方法了。我们打开编译之后的.class文件可以发现:
Main.class
public class Main {
public Main() {
System.out.println("父类的构造代码块");
System.out.println("父类的构造方法");
}
static {
System.out.println("父类的静态代码块");
}
}
son.class
class son extends Main {
public son() {
System.out.println("子类的构造代码块");
System.out.println("子类的构造方法");
}
public static void main(String[] args) {
new son();
System.out.println("----------------");
new son();
}
static {
System.out.println("子类的静态代码块");
}
}
构造代码块被放在了构造方法的第一句。这样就可以捋清为什么是这样的执行顺序了。
整个执行顺序如下:
- 实例化子类对象。
- 执行子类的构造方法,因为子类super指向父类的构造方法同时执行父类的构造方法。
- 由于静态代码块执行是优先于main方法的,所以先回去执行父类的静态代码块再执行子类的静态代码块。
- 然后此时super由于指向父类需要去执行父类的构造方法,且构造代码块会被转换到构造方法的第一行,所以此时就会执行父类的构造代码块以及构造方法。
- 当super执行完毕回到子类时,由于子类的构造代码块也被放到了构造方法中,且在super之后所以执行子类构造代码块再执行子类构造方法。
public class Main {
static {
System.out.println("父类的静态代码块");
}
{
System.out.println("父类的构造代码块");
}
public Main(){
System.out.println("父类的构造方法");
}
}
class son extends Main{
static {
System.out.println("子类的静态代码块");
}
{
System.out.println("子类的构造代码块");
}
public son(){
System.out.println("子类的构造方法");
}
public static void main(String[] args) {
new son();
System.out.println("----------------");
new son(); //再次实例化对象,注意执行结果。
}
}
执行结果:
父类的静态代码块
子类的静态代码块
父类的构造代码块
父类的构造方法
子类的构造代码块
子类的构造方法
----------------
父类的构造代码块
父类的构造方法
子类的构造代码块
子类的构造方法