介绍
在Java中,一个对象在可以被使用之前必须要被正确地初始化,这一点是Java规范规定的。在实例化一个对象时,JVM首先会检查相关类型是否已经加载并初始化
,如果没有,则JVM立即进行加载并调用类构造器完成类的初始化。在类初始化过程中或初始化完毕后,根据具体情况才会去对类进行实例化
。
抛砖引玉
下面先来看一个例子吧
先来看父类 Father.java
public class Father {
private int i = test();
static {
System.out.print("(1)");
}
private static int j = method();
Father() {
System.out.print("(2)");
}
{
System.out.print("(3)");
}
public int test() {
System.out.print("(4)");
return 1;
}
public static int method() {
System.out.print("(5)");
return 1;
}
}
再看看子类 Son.java
public class Son extends Father {
private int i = test();
private static int j = method();
static {
System.out.print("(6)");
}
Son() {
// super();//写或不写都在,在子类构造器中一定会调用父类的构造器
System.out.print("(7)");
}
{
System.out.print("(8)");
}
@Override
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) {
}
}
运行子类中的main函数,来看看结果:
(5)(1)(10)(6)(9)(3)(2)(9)(8)(7)
(9)(3)(2)(9)(8)(7)
Process finished with exit code 0
类的初始化过程
一个类要创建实例需要先加载并初始化该类
main方法
所在的类需要先加载和初始化
下面来改写Son.java中的main方法来看看:
main方法:
public static void main(String[] args) {}
main方法中没有任何执行语句,就是一个空的main方法
来看看执行结果:
(5)(1)(10)(6)
Process finished with exit code 0
对于上面的代码,这里因为没有new任何对象,所以这里执行的都是类的初始化:
-
父类的初始化<
clinit
>:- (1)j = method();
- (2)父类的静态代码块
-
子类的初始化<
clinit
>:- (1)j = method();
- (2)子类的静态代码块
这也就对应了运行结果上面的 (5)(1)(10)(6)
- 先初始化父类:(5)(1)
- 初始化子类:(10)(6)
一个子类要初始化需要先初始化父类
一个类初始化就是执行<clinit>()方法
- <clinit>()方法由
静态类变量
显示赋值代码和静态代码块
组成 - 类变量显示赋值代码和静态代码块代码从上到下顺序执行
- <clinit>()方法只执行一次
这里注意静态变量和静态代码块的顺序是按照代码的顺序来的,比如调换一下Father.java中二者的顺序,变成这样:
static {
System.out.print("(1)");
}
private static int j = method();
上述main方法为空的情况下的执行结果如下:
(1)(5)(10)(6)
Process finished with exit code 0
可以看到之前的(5)(1)变成了(1)(5)。
类的实例化过程
实例初始化就是执行<init>
()方法
- <init>()方法可能重载有多个,有几个构造器就有几个<init>方法
- <init>()方法由
非静态实例变量
显示赋值代码和非静态代码块
、对应构造器
代码组成 - 非静态实例变量显示赋值代码和非静态代码块代码从上到下顺序执行,而对应构造器的代码最后执行
- 每次创建实例对象,调用对应构造器,执行的就是对应的<init>方法
- <init>方法的首行是
super()
或super(实参列表),即对应父类的<init>方法
对于文章最开始的代码:
子类的实例化方法<init>:
- super()(最前) (9)(3)(2)
- i = test(); (9)
- 子类的非静态代码块 (8)
- 子类的无参构造(最后) (7)
注意!! 这里为什么执行父类的test()会输出(9)而不是(4)呢?
这是因为对象的多态性
:
- 子类如果重写了父类的方法,通过子类对象调用的一定是子类重写过的代码
- 非静态方法默认的调用对象是
this
- this对象在构造器或者说<init>方法中就是正在创建的对象
也就是说:
非静态方法前面其实有一个默认的对象this
this在构造器(或<init>)它表示的是正在创建的对象,因为这里是在创建Son对象,所以
test()执行的是子类重写的代码(面向对象多态)
那为什么method方法的时候输出的是父类的(5)而不是子类的(10)呢?
这是因为有三种方法不可以被重写:
- final方法
- 静态方法
- private等子类中不可见的方法
参考资料
https://blog.csdn.net/justloveyou_/article/details/72466416