结合具体代码解析类的初始化过程

结合代码解析类的初始化过程

这里需要注意几个点:1.各种静态(包括静态常量、静态变量、静态方法)都是一伙的,你只要在其它类里面用了其中一个 静态的东西,那么你引用的这个类中的所有静态都会被顺序加载,注意所有静态的引用顺序等价,JVM是按照代码书写的先后顺序来加载的。>

我们把类的加载分为两个阶段

在这里插入图片描述

第一个阶段:

加载main函数所在main类以及该类所继承的祖宗类(自然包括该类的父类,该类的父类的父类,该类的父类的父类的父类。。。)。就像一串葡萄一样,由main类开始往上摸,直到找到最顶端的父类,然后开始初始化。首先拎着最顶端的父类,然后由上往下加载这个类的所有静态常量、静态变量、静态代码块(加载这三者的时候按照的是代码书写时的先后顺序)。
练习题一

package Demo.JVM;

import static Demo.JVM.Father.print;


class Grandpa {
    static int Grandpa_Static01 = print("1");

    static {
        System.out.println("This is Granpa static block");
    }

    int age = print("66");
    final int child = print("b");
    static final int Grandpa_Static02 = print("2");

    Grandpa() {
        System.out.println("This is Grandpa() constructor");
    }

    static int print(String i) {
        System.out.println("This int is " + i);
        return 0;
    }
}

class Father extends Grandpa{
    static int Father_Static03 = print("3");

    static {
        System.out.println("This is Father static block");
    }

    static int Father_Static04 = print("4");

    Father() {
        System.out.println("This is Father() constructor");
    }

}

public class Son extends Father {
    static int Son_Static05 = print("5");

    static {
        System.out.println("This is Son    static block");
    }

    static int Son_Static06 = print("6");

    static int print(String i) {
        System.out.println("This int is " + i);
        return 0;
    }

    public static void main(String[] args) {
        System.out.println("This is main()");

    }
}

在查看答案之前自己先思考一下,拿出一个结果
运行答案

你注意到了吗?Grandpa类里面的int和final都没有被初始化。
很明显,这段代码的加载顺序就是我们上面提到过的,抓住最顶端的父类,然后加载静态静态常量、静态变量、静态代码块,。这一个类的加载完了才会跳到下一个类继续做同样的事

有人说加载一个既有静态常量也有静态变量的类的时候,是先把类里面的静态常量加载,加载完常量之后再去加载静态变量,而我们这里却不是这样。

第二个阶段:

按道理main函数里应该有许多的代码,这些代码初始化的顺序又是怎么样的呢?在完成第一个阶段初始化的基础上,从main函数的第一条语句开始,一条一条的解释。

如果main函数里面有主动引用,那么就去初始化引用的这个类,如果这个类还继承了那么就先初始化这个继承了的父类,那么也是从上开始时先把第一个类的实例化变量(int i = 0)和常量(fianl int =2;)全部都初始化了,再初始这个类的构造函数(到这里第一个类就初始完了),接下来按照同样的顺序初始接下来的类。有一点要注意静态只会被初始一次,如果在第一个步骤里面,有的静态被初始化了,这个阶段就不会被二次初始了。>

练习题二

package Demo.JVM;

import static Demo.JVM.print.print;

class print{
    public static int print(String str){
        System.out.println("This is " + str);
        return 1;
    }
}

class Grandpa {
    static int Static01 = print("Grandpa Static1");

    static {
        System.out.println("This is Grandpa static block");
    }

    int Int01 = print("Grandpa Int1");
    final int Final01 = print("Grandpa Final1");
    static final int Static02 = print("Grandpa static2");
    static{
        System.out.println("*************************Grandpa");
    }
    Grandpa() {
        System.out.println("This is Grandpa() constructor");
    }

}

class Father extends Grandpa{
    static int Static03 = print("Father Static3");

    static {
        System.out.println("This is Father static block");
    }

    static int Static04 = print("Father Static4");

    Father() {
        System.out.println("This is Father() constructor");
    }
    static{
        System.out.println("*************************Father");
    }

}

public class Son {
    static int Static05 = print("Son Static5");

    static {
        System.out.println("This is Son static block");
    }
    Son(){
        System.out.println("This is Son() constructor");
        //这个构造函数并没有被调用,因为没有谁调用了Son类
    }
    
    static int Static06 = print("Son Static6");

    public static void main(String[] args) {
        System.out.println("*************************main");
        System.out.println("This is main()");
        System.out.println("*************************main");
        Father father = new Father();
        //这里有一个主动引用所以要去初始化Fahter这个类
        //注意在这一段代码里面Son 没有再继承Father了
    }
}

运行答案

分析上面这一段代码

通过上一段代码我们知道,它也符合了我们第一阶段的解释。他先执行了main类的static,不过并没有执行Son的构造函数,因为没有发生主动调用。在此之后开始执行main方法的代码。



















第一个练习运行答案↓↓↓

This is Grandpa Static1
This is Grandpa static block
This is Grandpa static2
*************************Grandpa
This is Father Static3
This is Father static block
This is Father Static4
*************************Father
This is Son Static5
This is Son static block
This is Son Static6
*************************main
This is main()
*************************main

返回练习题一
主动引用↓↓↓

* 类的主动引用(一定会发生类的初始化)
    * new一个类的对象
    * 调用类的静态成员(除了final常量)和静态方法
    * 使用java.lang.reflect包的方法对类进行反射调用
    * 当虚拟机启动,java Demo01,则一定会初始化Demo01类,说白了就是先启动main方法所在的类
    * 当初始化一个类,如果其父类没有被初始化,则先初始化它父类
* 类的被动引用(不会发生类的初始化)
    * 当访问一个静态域时,只有真正声名这个域的类才会被初始化
        * 通过子类引用父类的静态变量,不会导致子类初始化
    * 通过数组定义类的引用,不会触发此类初始化
    * 引用常量不会触发此类的初始化(常量在编译阶段就存入调用类的常量池中了)

第二个练习运行答案↓↓↓

This is Son Static5
This is Son static block
This is Son Static6
*************************main
This is main()
*************************main
This is Grandpa Static1
This is Grandpa static block
This is Grandpa static2
*************************Grandpa
This is Father Static3
This is Father static block
This is Father Static4
*************************Father
This is Grandpa Int1
This is Grandpa Final1
This is Grandpa() constructor
This is Father() constructor

返回练习题一

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值