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方法中就是正在创建的对象

posted @ 2020-04-10 22:32  羊37  阅读(897)  评论(0编辑  收藏  举报