Java中的类初始化和实例初始化
我也是小白,各位倘若看到一定要带思考的去看。
一、背景
存在类Father和类Son,其中类Son继承了Father类。
1.父类: Father
/**
* @Author Yiang37
* @Date 2020/4/9 23:44
* Description:
*/
public class Father {
private final int i = test();
private static final int j = method();
static {
System.out.print("(1)");
}
{
System.out.print("(3)");
}
Father() {
System.out.print("(2)");
}
public int test() {
System.out.print("(4)");
return 1;
}
public static int method() {
System.out.print("(5)");
return 1;
}
}
2.子类: Son
package cn.yang37.classloading.loadstep;
/**
* @Author Yiang37
* @Date 2020/4/9 23:44
* Description:
*/
public class Son extends Father {
private final int i = test();
private static final int j = method();
static {
System.out.print("(6)");
}
{
System.out.print("(8)");
}
Son() {
System.out.print("(7)");
}
@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) {
// Son s1 = new Son();
// System.out.println();
// Son s2 = new Son();
}
}
二、初始化
初始化包括?
成员变量赋初值、代码块、构造器
注意方法是被调用的,有人调用它它才执行相应的东西。
三、类初始化
在一开始,注释掉son类main方法中代码
public static void main(String[] args) {
// Son s1 = new Son();
// System.out.println();
// Son s2 = new Son();
}
执行结果如下:
(5)(1)(10)(6)
1.规则
1.1 实例初始化之前先要进行类的初始化(加载类),即执行一些static相关的操作.
所以你执行空的main()也会输出结果,因为在进行类的初始化.
1.2 子类初始化要先初始化其父类
即先执行的Father的,再执行Son的.
1.3 类初始化即是执行clinit(ClassInit)方法
- 加载静态变量/静态代码块
- 按顺序加载(上到下的顺序执行)
- 这个clinit方法只执行一次,即类只初始化一次即可.
2.扩展
2.1 将父类中的static代码块放到静态变量之前,可以看到结果按顺序改变。
package cn.yang37.classloading.loadstep;
/**
* @Author Yiang37
* @Date 2020/4/9 23:44
* Description:
*/
public class Father {
private final int i = test();
static {
System.out.print("(1)");
}
private static final int j = method();
{
System.out.print("(3)");
}
Father() {
System.out.print("(2)");
}
public int test() {
System.out.print("(4)");
return 1;
}
public static int method() {
System.out.print("(5)");
return 1;
}
}
先输出1再输出5,即按顺序加载.
(1)(5)(10)(6)
四、实例初始化
取消Son类中main方法的第一句注释,即:
进行new操作,即实例的初始化.
public static void main(String[] args) {
Son s1 = new Son();
// System.out.println();
// Son s2 = new Son();
}
此时将会先进行类初始化,再进行实例初始化,结果如下。
(5)(1)(10)(6)(9)(3)(2)(9)(8)(7)
1.规则
1.1 实例初始化即是执行init方法
Java文件在编译后会在字节码文件中生成init方法,该方法被称之为实例构造器。init方法是在对象实例化时执行的。
1.2 方法中调用几个构造器就执行几个init方法,每次创建对象都会执行相应的init方法。
1.3 init方法由非静态实例变量显式赋值、非静态代码块和相应的构造器组成。
1.4 非静态实例变量赋值与非静态代码块按上到下的顺序执行,构造器在最后执行。
构造器嘛,要用到变量之类的,肯定最后阶段啦.
1.5 init方法的首行是super()或者带参数的super(),即父类init方法。
2.扩展
2.1 父类中的test()方法输出的是(4),为何父类中结果为932?
非静态方法前有一个默认的对象this
this在构造器(或者init方法)表示的是正在创建的对象。
这里是在创建Son类的对象,所以执行的是重写后的代码。
2.1 取消main中的所有注释
public static void main(String[] args) {
Son s1 = new Son();
System.out.println();
Son s2 = new Son();
}
运行结果:
(5)(1)(10)(6)(9)(3)(2)(9)(8)(7)
(9)(3)(2)(9)(8)(7)
可以看到(9)(3)(2)(9)(8)(7)执行了两次,因为new Son()执行了两次.
五、总结
初始化操作包括成员变量、静态/非静态代码块和构造器。
1.先类初始化,再实例初始化。
2.都是先父类再子类。
3.类初始化关键词"静态"。
4.实例初始化关键词"非静态",构造器在最后。
5.注意构造器和实例初始化init方法中的this对象。
六、补充
1.哪些方法不能被重写?
final方法
静态方法
private等子类中不可见方法
2.多态性的体现?
子类若重写了父类的方法,则通过子类对象调用的一定是子类重写过的方法
非静态方法默认的调用对象是this
this对象在构造器或者init方法中就是正在创建的对象