前言:
本文通过引用一道经典的Java面试题,解释Java类、实例对象的初始化和实例化等知识。主要内容有以下三点:
1.类初始化过程(先)
2.实例初始化过程(后)
3.方法重写(注意)
首先看题目,分析输出结果是什么:
父类:
public class Father {
private int i = test();
private static int j = method();
static {
System.out.println("(1)");
}
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;
}
}
子类:
public class Son extends Father {
private int i = test();
private static int j = method();
static {
System.out.println("(6)");
}
Son() {
System.out.println("(7)");
}
{
System.out.println("(8)");
}
public int test() {
System.out.println("(9)");
return 1;
}
public static int method() {
System.out.println("(10)");
return 1;
}
//TEST
public static void main(String[] args) {
Son son = new Son();
System.out.println();
Son son1 = new Son();
}
}
1.类初始化过程
(1)主方法main所在的类要先加载初始化(即使main中没有内容)
(2)创建实例对象的时候会先加载初始化类
(3)一个子类初始化需要先初始化它的父类
(4)类初始化其实就是执行<clinit>()方法,它由静态类变量和静态代码块组成,执行顺序从上到下。
综上,首先主方法main()所在的类先加载初始化(1),而加载这个子类前要先加载初始化其父类(3),也即是Father的静态类变量j=method()【输出1】,静态代码块【输出5】,从上到下执行;父类加载完成后开始加载Son的静态类变量j=method()【输出10】,静态代码块【输出6】,从上到下执行(4)。
2.实例初始化过程
(1)实例初始化实际就是执行<init>()方法,<clinit>一个类只有一个,但是<init>有多少个构造器就有多少个(可能有带参、不带参多个构造器)。它由静态类变量和静态代码块和构造器三部分组成。其中,非静态实例变量和非静态代码块执行顺序从上到下,构造器最后执行。
(2)<init>()方法首行都是super(),不管写没写,它都是存在的。
综上,子类实例化方法分为以下四步:
- 1、super() => 父类的实例化(又是这三步)
- 2、子类非静态实例变量,非静态代码块执行顺序从上到下
- 3、子类构造器
1、首先是super(),父类实例化,非静态变量i=test,但是这里涉及到方法重写,非静态方法前的默认的调用对象是this,而this指的是正在创建的对象,也就是子类对象,所以i=test输出的是子类中的test方法:【输出9】;其次是父类非静态代码块:【输出3】,然后是父类构造器最后:【输出2】
2、子类的非静态变量i=test方法:【输出9】;子类非静态代码块:【输出8】;从上到下顺序执行
3、子类构造器最后:【输出7】
第二个Son对象创建,又重新一次,输出【932987】,所以总的输出位【1 5 10 6 9 3 2 9 8 7 9 3 2 9 8 7】
3.关于方法重写
如果子类重写了父类的方法,通过子类对象调用的就一定是子类重写过的代码,可能会疑问,为什么方法test()被子类重写,而method方法不会被重写呢,那么什么方法不可以被重写?
(1)final方法不可以被重写
(2)静态方法不能被重写
(3)private等子类中不可见的方法不可被重写