类和实例的初始化
面试题
代码如下:
package com.jk.test;
/**
* @author Mr.JK
* @create 2020-06-15 21:41
*/
public class Father {
private int i = test();
private static int j = method();
static {
System.out.print("(1)");
}
Father(){
System.out.print("(2)");
}
{
System.out.print("(3)");
}
private int test() {
System.out.print("(4)");
return 1;
}
private static int method() {
System.out.print("(5)");
return 1;
}
}
package com.jk.test;
/**
* @author Mr.JK
* @create 2020-06-15 21:41
*/
public class Son extends Father {
private int i = test();
private static int j = method();
static {
System.out.print("(6)");
}
Son(){
System.out.print("(7)");
}
{
System.out.print("(8)");
}
public int test(){
System.out.print("(9)");
return 1;
}
public static int method(){
System.out.print("(10)");
return 1;
}
public static void main(String[] args) {
Son son1 = new Son();
System.out.println();
Son son2 = new Son();
}
}
main函数执行后,大家可以试试看输出的顺序是什么?
类初始化过程
1、一个类的创建实例需要先加载并初始化该类
- main方法所在的类需要先加载和初始化
2、一个子类要初始化需要先初始化父类
3、一个类初始化就是执行<clinit>()方法
- <clinit>()方法由静态类变量显示赋值代码和静态代码块组成
就是说在main方法启动的时候,就会去初始化类
因为在父类和子类中都有静态变量和静态代码块,静态变量和静态代码块顺序是根据代码书写的顺序执行的所以:
执行顺序为 (5)(1)(10)(6)
实例的初始化过程
实例初始化就是执行<init>()方法
- <init>()方法可能重载有多个,有几个构造器就有几个<init>()方法
- <init>()方法由非静态实例变量显示赋值代码和非静态代码块、对应构造器代码组成
- 非静态实例变量显示复制代码和非静态代码块代码从上到下顺序执行,而对应构造器的代码最后执行
- 每次创建实例对象,调用对应构造器,执行的就是对应的<init>()方法
- <init>()方法的首行是super()或super(实参列表),即对应父类的<init>()方法
就是说在调用构造函数进行实例的初始化时,不管有没有super关键字,都会调用父类的构造器先进行初始化,然后在初始化父类构造器后,再执行类中显示赋值代码和非静态代码块 比如Son子类的:
private int i = test();
{
System.out.print("(8)");
}
public static int method(){
System.out.print("(10)");
return 1;
}
最后才是Son子类的构造器
Son(){
System.out.print("(7)");
}
所有根据以上的顺序得出的全部顺序为
(5)(1)(10)(6)(4)(3)(2)(9)(8)(7)
(4)(3)(2)(9)(8)(7)
以为到这里结束了嘛,其实这里还有一个坑,就是多态性,方法的重写!
方法的重写Override
1、哪些方法不可以被重写
- final方法
- 静态方法
- private等子类中不可见方法
2、对象的多态性
- 子类如果重写了父类的方法,通过子类对象调用的一定是子类重写过的代码
- 非静态方法默认的调用对象是this
- this对象在构造器或者说方法中就是正在创建的对象
也就是说在初始化父类实例时,初始化变量时候调用test()方法,因为是非静态方法,所有默认调用对象this,是当前对象,也就是Son对象 所以实际执行的是子类重写的方法
最终的结果顺序为
一叶知秋,穿插了类与实例的初始化过程。
此文章根据尚硅谷视频加上个人理解与实践。