尚硅谷java面试题视频之类初始化和实例初始化笔记
原视频:尚硅谷经典java面试题第一季
首先先来看一段代码,思考其执行顺序:
public class Father {
private int i = test();
private static int j = method();
static {
System.out.println("父类——静态代码块");
}
Father() {
System.out.println("父类——构造器");
}
{
System.out.println("父类——代码块");
}
public int test() {
System.out.println("父类——变量");
return 1;
}
public static int method() {
System.out.println("父类——静态变量");
return 1;
}
}
public class Son extends Father {
private int i = test();
private static int j = method();
static {
System.out.println("子类——静态代码块");
}
Son() {
System.out.println("子类——构造器");
}
{
System.out.println("子类——代码块");
}
public int test() {
System.out.println("子类——变量");
return 1;
}
public static int method() {
System.out.println("子类——静态变量");
return 1;
}
public static void main(String[] args) {
System.out.println("——————");
Son s1=new Son();
System.out.println("——————");
Son s2=new Son();
}
}
执行结果:
父类——静态变量
父类——静态代码块
子类——静态变量
子类——静态代码块
——————
子类——变量
父类——代码块
父类——构造器
子类——变量
子类——代码块
子类——构造器
——————
子类——变量
父类——代码块
父类——构造器
子类——变量
子类——代码块
子类——构造器
开始讲解:
类初始化过程
-
一个类要创建实例需要先加载并初始化该类
main方法所在的类需要先加载和初始化;
-
一个子类要初始化需要先初始化父类
-
一个类初始化就是执行<clinit>方法
<clinit>方法由静态类变量显示赋值代码和静态代码块组成;
类变量显示赋值代码和静态代码块代码从上到下顺序执行;
<clinit>方法只执行一次。
实例初始化过程
- 实例初始化就是执行<init>方法;
- <init>方法可能重载有多个;有几个构造器就有几个<init>方法;
- <init>方法由非静态实例变量显示赋值代码块,非静态代码块,对应构造器代码组成;
- 非静态实例变量显示赋值代码块和非静态代码块代码从上到下顺序执行,而对应的构造器的代码最后执行;
- 每次创建实例对象,调用对应构造器,执行的就是对应的<init>方法;
- <init>方法的首行是super()或super(实参列表),即对应父类的<init>方法;
方法的重写Override
- 哪些方法不能被重写
final方法
静态方法
private等子类中不可见方法 - 对象的多态性
子类如果重写了父类的方法,通过子类对象调用的一定是子类重写过的代码;
非静态方法默认的调用对象时this;
this对象在构造器(或<init>)方法中就是正在创建的对象。
文章开头代码的执行顺序
根据上述类初始化过程和实例初始化过程,程序运行顺序应该为:
- Son类中包含main方法,不管main方法中是否有内容,都要先加载和初始化Son类;
- 初始化Son类需要先初始化Father类;
- 父类的初始化<clinit>:
加载j=method(),输出“父类——静态变量”;
加载父类静态代码块,输出“父类——静态代码块”;
- 父类的初始化<clinit>:
- 子类的初始化<clinit>:
加载j=method(),输出“子类——静态变量”;
加载子类的静态代码块,输出“子类——静态代码块”;
- 初始化Son类需要先初始化Father类;
- 执行main方法中的Son s1=new Son();
- 子类的实例化方法<init>:
- 加载super(),即父类的实例化方法:
- 加载super(),即父类的实例化方法(默认继承Object类);
- 加载i=test(),非静态方法前边其实有一个默认的对象this,this在构造器(或<init>)表示正在创建的对象,因为这里是在创建Son对象,所以test执行的是子类重写的代码(面向对象多态),输出“子类——变量”;
- 加载父类的非静态代码块,输出“父类——代码块”;
- 加载父类的无参构造器,输出“父类——构造器”;
- 加载i=test(),输出“子类——变量”;
- 加载子类的非静态代码块,输出“子类——代码块”;
- 加载子类的无参构造器,输出“子类——构造器”;
- 加载super(),即父类的实例化方法:
- 子类的实例化方法<init>:
- 执行main方法中的Son s2=new Son();
- 因为每次创建实例对象,调用对应构造器,执行的就是对应的<init>方法,所以本次执行跟第一次执行Son s1=new Son()时完全一样;
- 输出“子类——变量”;
- 输出“父类——代码块”;
- 输出“父类——构造器”;
- 输出“子类——变量”;
- 输出“子类——代码块”;
- 输出“子类——构造器”;