java注解和反射(六)---类初始化及类加载器

1. 类初始化:

什么时候会发生类初始化?
[1] 类的主动引用 (一定会发生类的初始化)

  • 当虚拟机启动,先初始化main方法所在的类
  • new 一个类的对象
  • 调用类的静态成员(除了finial常量) 和静态方法
  • 使用java.lang.reflect 包的方法对类进行反射调用
  • 当初始化一个类。如果父类没有初始化,则先回初始化它的父类

[2] 类的被动引用(不会发生类的初始化)

  • 当访问一个静态域时,只有真正声明这个域的类才会初始化,如:当通过子类引用父类的静态变量,不会导致子类初始化。
  • 通过数组定义类引用,不会触发此类的初始化。
  • 引用常量不会触发此类的初始化(常量是在链接阶段存入调用的常量池
package com.gs.reflection;

//测试类什么时候会初始化
public class Test06 {
    static {
        System.out.println("Main类被加载");
    }

    public static void main(String[] args) throws ClassNotFoundException {

        System.out.println("===========会产生类的初始化================");
         //1. 主动引用
         Son son = new Son();
         //2.反射也会产生主动引用
        Class.forName("com.gs.reflection.Son");

        //3.调用类的静态成员
        System.out.println(Son.m);


        System.out.println("===========不会产生类的初始化================");
        //不会产生引用的方法
         //4.子类访问父类的静态域 ===>只有父类会初始化
        System.out.println(Son.b);
        //5.通过数组定义引用 ===>不会初始化
        Son[] array = new Son[5];
        //6.引用常量 ===>不会初始化
        System.out.println(Son.M);
        //7.调用finial时,不会进行类的初始化
        System.out.println(Son.M);
    }
}

class Father{
    static int b = 2;
    static {
        System.out.println("父类被加载");
    }
}
class Son extends Father{
    static {
        System.out.println("子类被加载");
        m = 300;
    }
    static int m = 100;
    static final int M = 1;
}

代码解析:
上面的代码1~7逐一测试的话你会发现 1~ 3在进行调用的时候,不仅会对子类进行初始化,还会对父类初始化(父类的初始化先于子类的初始化),调用4的时候,只有父类会初始化,子类不会。而调用5~7时,父类和子类都不会初始化。这也很好地印证了上面的结论。

2. 类加载器的作用

[1] 类加载的作用: 将class字节码内容加载到内存中,并将这些静态数据转化为方法区内的运行时数据结构,然后在堆中生成一个代表这个类的java.lang.Class对象。作为方法区中类数据访问入口。
[2] 类缓存:标准的JavaSE类的加载器可以按要求查找类,但一旦某个类加载到类加载器中,它将为维持加载(缓存)一段时间。不过JVM垃圾回收机制可以回收这些Class对象。
在这里插入图片描述
类加载器的作用就是用来把类(class) 装载进内存的。JVM规范定义了如下类型的加载器。
在这里插入图片描述
测试代码

package com.gs.reflection;


public class Test07 {
    public static void main(String[] args) throws ClassNotFoundException {

        //1.获取系统类的加载器
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
        System.out.println(systemClassLoader);

        //2.获取系统类加载器的父类加载器 --->扩展类加载器
        ClassLoader parent = systemClassLoader.getParent();
        System.out.println(parent);

        //3.获取扩展类加载器的父类加载器 -->跟加载器(C/C++)(无法获取的)
        ClassLoader parent1 = parent.getParent();
        System.out.println(parent1);

        //测试当前类是哪个加载器加载的 (是由系统加载器加载的)
        ClassLoader classLoader = Class.forName("com.gs.reflection.Test07").getClassLoader();
        System.out.println(classLoader);
        //测试JDK内置的类是哪个类加载器 (是由跟加载器加载的)
        ClassLoader classLoader1 = Class.forName("java.lang.Object").getClassLoader();
        System.out.println(classLoader1);

        //如何获得系统加载器可以加载的路径
        System.out.println(System.getProperty("java.class.path"));

        //双亲委派机制
        /**如果你自定义了java.lang.String的包,它并不会运行,它会一直向上找是否
         * 有相同的包。如果有,你这个包将不会被加载
         **/
    }
}

运行结果
在这里插入图片描述

发布了86 篇原创文章 · 获赞 6 · 访问量 7264

猜你喜欢

转载自blog.csdn.net/TheWindOfSon/article/details/105538775