类加载过程 --父子类构造方法、静、非静代码块/变量加载顺序问题

概述

弄清楚父子类的加载过程,各个类中静态/非静态变量、静态/非静态代码块、构造方法、普通方法等的加载过程对于推断最终的结果非常重要。

结论

先给出总的结论。

类初始化过程

(1)一个类要创建实例需要先加载并初始化该类。
main()方法所在的类需要先加载并初始化。
(2)一个子类要初始化需要先初始化父类。
(3)一个类初始化就是执行<clinit>()方法。
<clinit>()方法包括:静态变量和静态代码块,他们的加载顺序同执行顺序。且只执行一次

总的来说,类加载的时候先加载父类,加载的内容是静态代码块和静态变量,加载顺序同代码顺序,只执行一次。

实例初始化过程

实例初始化就是执行<init>()方法。
<init>()方法包括:非静态变量、非静态代码块、对应构造器。
②执行顺序:非静态变量 和 非静态代码块按照代码顺序,构造器是最后执行。
③每次创建实例,调用对应构造器,就是去执行<init>()方法。
④执行的时候也是先执行父类对应的内容,然后再执行子类。
⑤非静态变量初始化的时候还要考虑重写问题。如果存在重写,是执行子类中的内容。

总的来说,父类先执行,执行内容包括:非静态代码块、非静态变量、构造方法。执行顺序:非静态代码块 和 非静态变量按照代码顺序,构造器最后执行。并且,非静态方法的时候需要考虑重写的问题,重写之后就是执行子类对应重写的方法。 若创建了多个实例,就会执行对应次数的<init>()方法。

方法重写

另外,关于方法重写的内容,以下这些情况,方法是不能被重写的,包括:
(1)final修饰的方法。
(2)静态方法。
(3)private修饰的方法,导致子类中不可见,所以不可以被重写。

案例

父类:

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)");
    }

    //非静态变量调用
    public int test() {
        System.out.print("(4)");
        return 1;
    }

    //静态变量方法调用
    private static int method() {
        System.out.print("(5)");
        return 1;
    }
}

子类:

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;
    }

    //静态变量方法调用
    private 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();
}

分析过程

1.会进行类加载过程。 无论你是否有使用这个对象去创建实例,在类加载的时候就会执行。
类加载的时候,是先加载父类,然后再加载子类。此时执行<clinit>()方法,加载的是静态相关的内容,静态变量和静态代码块。这二者的加载顺序和代码顺序一致,谁在上面谁就会先加载。
在这里,静态变量在前,静态代码块在后。所以会先加载静态变量j,在加载的时候会调用method()方法,即:输出(5)
然后,加载静态代码块,输出(1)
此时,父类加载已经完成,就取加载子类中对应的内容,加载顺序同父类,静态变量和静态代码块。
首先,加载静态变量j,调用method()方法,输出(10)
然后,加载静态代码块,输出(6)

所以,在类加载后的结果就是: (5)(1)(10)(6)。

2.类加载完成之后,才会进入到实际的创建实例的过程。

创建实例的时候,是执行<init>()方法。包括:非静态变量、非静态代码块、构造方法。构造方法最后,剩下的按照代码顺序。且是先执行父类的,因为有super()方法。

首先,父类有非静态变量 和 非静态代码块,按照顺序执行,所以先初始化 非静态变量i,这个时候回调用test()方法,但是这里需要注意一个问题,这里(父类)的test()方法被子类重写了,所以最终执行的是子类的test()方法。所以结果是:输出(9)
然后,进行非静态代码块,输出(3)
最后,是构造方法,输出(2)

上面的父类完了之后,就会到子类进行,顺序也和上面一样。
首先,初始化非静态变量i,执行自己的test()方法,输出(9)
然后,初始化非静态代码块,输出(8)
最后,子类构造方法,输出(7)

所以,创建一次Son实例,输出的内容是:(9)(3)(2)(9)(8)(7)。
因为调用的到时候创建了两个实例,所以<init>()方法被执行两次,所以会再一次输出:(9)(3)(2)(9)(8)(7)。

最终,类加载和实例过程结合起来的输出就是:

(5)(1)(10)(6)(9)(3)(2)(9)(8)(7)
(9)(3)(2)(9)(8)(7)
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值