类加载系列之理解类加载

类的阶段

在Java代码中,类型的加载、连接和初始化都是在程序运行期完成的。类有以下几个阶段:

  • 类加载:即查找并加载类的二进制数据
  • 类连接:
    • 验证:确保被加载类的正确性
    • 准备:为类的静态变量分配内存,并将其初始化为默认值
    • 解析:类的符号引用转换为直接引用
  • 类初始化:为类的静态变量赋予正确的初始值,运行static快中的代码。
  • 类使用
  • 类卸载

类加载

类加载:类的加载是指将类的.class文件的二进制数据读入内存中,将其放在运行时数据区的方法区中,然后在内存中创建了一个java.lang.Class对象(HotSpot中这个Class对象在方法区)用来封装类在方法区的数据结构。(比如反射就是用的Class对象)

加载.class文件的方式

  1. 从本地系统磁盘直接加载(classpath下的文件)
  2. 通过网络下载.class文件
  3. 通过zip、jar等归档文件加载.class文件(多用于第三方的jar包)
  4. 将java源文件动态编译成.class文件(比如动态代理,运行期生成代理类的类加载过程)

类的初始化

java程序对类的使用方式有两种:

  1. 主动使用
  2. 被动使用

类初始化的时机

所有的jvm实现都必须保证在每个类或接口都被程序“首次主动使用”时才进行初始化。(这里是类初始化

使用类

主动使用类有下面几种方式:

  • 创建类的实例
  • 访问类或者接口的静态变量,或者对这个静态变量进行赋值。
  • 调用类的静态方法
  • 反射加载一个类
  • 初始化一个类的子类,则父类也算被主动调用。
  • 启动类,比如test
  • 动态语言支持,java.lang.invoke.MethodHandle实例的解析结果的句柄对应类没有初始化,则进行初始化

被动使用:除了主动使用类的方式使用类或接口,都会被看做是类的被动使用,不会导致类的初始化

一个例子解释类的主动使用和被动使用

public class TestClassLoading {

    public static void main(String[] args) {

        // (1)并不会初始化类 Children 因为没有主动使用 Children;会初始化Parent 因为访问了str1
        System.out.println(Children.str1);

        System.out.println("===========================");
        
        // 会初始化Parent 也会初始化Children。因为访问了str2,要初始化Children,又因为子类被初始化,所以父类也被主动使用,进行初始化
        System.out.println(Children.str2);
    }

    static class Parent {
        static String str1 = "hello";

        static {
            System.out.println("Parent初始化。。。。");
        }
    }

    static class Children extends Parent {
        static String str2 = "world";

        static {
            System.out.println("Children初始化。。。。");
        }
    }


}

对应的输出:

Parent初始化。。。。
hello
===========================
Children初始化。。。。
world

你可能感兴趣的:(jvm,jvm,java,类)