写在前面:其实这个过程在我最开始学习JVM的时候,就有学习过。但是当时只是明白了一个概念,知道有这样的一个过程。然后就没有了


直到遇到了这道题,让我对这个过程的理解直接提升了一个层次(夸张的手法),不过真的是有很大的帮助,希望对你们也是!!!


多做题好处多多呀。

文章目录

  • ​​1、加载的基本概念​​
  • ​​1.1、类的初始化过程​​
  • ​​1.2、类的实例化过程​​
  • ​​1.3、方法的重写Override​​
  • ​​2、测试程序分析​​
  • ​​2.1、第一步​​
  • ​​2.2、第二布​​
  • ​​2.3、最终的测试代码​​



1、加载的基本概念

如果有JVM的基础,那么接下来的概念你就能更好的理解



1.1、类的初始化过程

  1. 一个类要创建实例需要先加载并初始化该类
  • main方法所在的类需要先加载和初始化
  1. 一个子类要初始化需要先初始化父类
  2. 一个类初始化就是执行 <clint>() 方法
  • <clint>() )方法由①静态类变量显示赋值代码②静态代码块组成
  • 类变量显示赋值代码和静态代码块代码从上到下顺序执行
  • <clint>() 方法只执行一次


1.2、类的实例化过程

  1. 实例初始化就是执行 <init>() 方法
  • <init>() 方法可能重载有多个,有几个构造器就有几个 <init>() 方法
  • <init>() 方法由①非静态实例变量显示赋值代码②非静态代码块③对应构造器代码组成
  • 非静态实例变量显示赋值代码和非静态代码块代码从上到下顺序执行,而对应构造器的代码最后执行
  • 每次创建实例对象,调用对应构造器,执行的就是对应的 <init>() 方法
  • <init>() 方法的首行是super() 或 super(实参列表) ,即对应父类的 <init>() 方法


1.3、方法的重写Override

  1. 哪些方法不可以被重写
  • final方法
  • 静态方法
  • private等子类中不可见的方法
  1. 对象的多态性
  • 子类如果重写了父类的方法,通过子类对象调用的一定是子类重写后的代码
  • 非静态方法默认的调用对象是this
  • this对象在构造器或者说 <init>() 方法中就是正在创建的对象



2、测试程序分析



2.1、第一步

直接调用该方式

类的初始化和实例化之间的关系_初始化

执行过程:

  1. 首先初始化main方式所在的类,即Son类
  2. Son类作为子类,其又会去调用对应的父类Father
  3. 在Father类中,它会先去执行static方法,并且按照对应的顺序自上而下的执行
  4. 所以会先执行变量 j 对应的方法method,输出5
  5. 然后执行静态代码块中的代码,输出对应的数字 1
  6. 初始完我们的父类以后,回到子类中,继续完成初始化
  7. 与父类的执行方式相同,先会输出 j 对应的method方法,即输出10
  8. 然后再去执行静态代码块中的语句,输出对应的数字6

补充:一个类在加载以前是需要初始化的,并且这个初始化在程序的整个执行过程中只会初始化一次,在字节码层面就是执行对应的<clint>方法。以至于我们的main方法没有方法体,他也会输出对应的结果。

设想如果我们将我们的main方法单独弄成一个test类,当我们去实例化一个类的时候,也会执行<clint>方法,即完成对应的初始化



2.2、第二布

实例化我们的子类Son

类的初始化和实例化之间的关系_初始化_02

执行过程:

  1. 我们都知道我们实例化一个子类的时候,子类的构造方法中存在一个被隐藏super()方法,即会先去执行父类的实例化
  • 先去执行Father类构造方法中的super方法,调用其父类,这个为指向Object
  • 再执行非静态变量,即 i = test() 语句,由于 test() 方法被重写,输出子类的 test() 方法体,即输出8
  • 然后再执行该类的非静态代码块,输出4
  • 最后再执行该类的无参构造代码,输出2
  1. 再执行非静态变量,即 i = test() 语句,输出8
  2. 然后再执行子类的非静态代码块,输出6
  3. 最后才是子类的无参构造方法,输出7


注意:在父类的方法中,method方法由于是static方法,所以在类初始化的时就完成,所以不影响。但是test方法是一个非静态的方法,并且它的方法被子类进行了重写,这里就涉及到了一个多态的概念。看似我们调用的是父类的test方法,实则输出的是子类中重写后的 test() 方法.

  • 非静态方法前面有一个默认的对象this
  • this在构造器(或<init>() )它表示的是正在创建的对象,因为这里是在创建Son对象,所以
  • test() 执行的是子类重写的代码(面向对象多态)
  • 所以这个我们看到的父类的i = test() 执行的是子类重写的test() 方法(如果是三级继承关系,那么会调用被最后重写的那个方法)

类的初始化和实例化之间的关系_System_03



2.3、最终的测试代码

父类:Father

package pers.mobian.questions03;

public class Father {
private int i = test();
private static int j = method();

static {
System.out.println("(1)");
}

public Father() {
System.out.println("(2");
}

{
System.out.println("(3)");

}

public int test() {
System.out.println("(4)");
return 1;
}

public static int method() {
System.out.println("(5)");
return 1;
}
}



子类:Son

package pers.mobian.questions03;

public class Son extends Father {
private int i = test();

private static int j = method();

static {
System.out.println("(6)");
}

public Son() {
System.out.println("(7)");
}

{
System.out.println("(6)");
}

public int test() {
System.out.println("(8)");
return 1;
}

public static int method() {
System.out.println("(10)");
return 1;
}

public static void main(String[] args) {
System.out.println("---");
Son son = new Son();
System.out.println("---");
Son son1 = new Son();
}
}



执行结果:

//初始化类的时候执行的代码
(5)
(1)
(10)
(6)
---
//实例化Son类时执行的代码
(8)
(3)
(2
(8)
(6)
(7)
---
//再次实例化Son类时执行的代码
(8)
(3)
(2
(8)
(6)
(7)