关于jvm加载类的实现顺序

本文介绍了一个使用Java实现的枚举缓存类CachingEnumResolver。该类采用单例模式,并利用静态初始化块预填充枚举缓存。文章通过一个示例程序展示了如何初始化并获取枚举缓存。

public class CachingEnumResolver {

    private static Map CODE_MAP_CACHE;
    private static CachingEnumResolver SINGLE_ENUM_RESOLVER = new CachingEnumResolver();

    static {
        CODE_MAP_CACHE = new HashMap();
        CODE_MAP_CACHE.put( "0", "北京市" );
    }

    public static CachingEnumResolver getInstance() {
        return SINGLE_ENUM_RESOLVER;
    }

    public void initEnums() {

        if( null == CODE_MAP_CACHE ) {
            System.out.println( "CODE_MAP_CACHE为空,问题在这里开始暴露." );
            CODE_MAP_CACHE = new HashMap();
        }
        CODE_MAP_CACHE.put( "1", "北京市" );
        CODE_MAP_CACHE.put( "2", "云南省" );

    }

    public CachingEnumResolver() {
        initEnums();
    }

    public Map getCache() {
        return Collections.unmodifiableMap( CODE_MAP_CACHE );
    }

    public static void main(String[] ars){
        CachingEnumResolver resolver = CachingEnumResolver.getInstance();
        Map map = resolver.getCache();
        for( Object key : map.keySet() ) {
            System.out.print( key+"------------->" );
            System.out.print( map.get( key ));
        }
    }
}

 

结果:

CODE_MAP_CACHE为空,问题在这里开始暴露.
0------------->北京市

看了一篇文章《神秘的类和对象初始化过程》,又熟悉了一遍关于jvm加载类的实现过程。总结了一下关于类的加载顺序:

开始

1.静态字段
2.静态块

//如果你的调用为new,则继续执行下面的。如果没有new,而是通过静态方法第一次访问该类,则到此处就返回了。
3.字段(非静态块)
4.构造函数


返回

 

父类1-->父类2-->子类1-->子类2-->父类3-->父类4-->子类3-->子类4

(以上说明先类的后对象的.在此基础上先父类,后子类)-->先类先父
根据上面的例子说明一下:上面刚刚初始化静态字段的时候就有new的调用,所以先调用构造函数来执行。完成后执行静态块把原来赋好的值给覆盖掉了,结果才出现那种情况。

 

### JVM加载机制详解 JVM加载机制是Java运行时环境的核心组成部分,负责将的字节码文件加载到内存中,并在运行时对其进行验证、准备、解析、初始化等操作。加载机制的主要目标是确保的正确性和安全性,同时提供灵活的加载方式以支持动态扩展和模块化开发。 #### 加载过程 JVM加载过程分为以下几个阶段: 1. **加载(Loading)** 在加载阶段,加载器通过`ClassLoader`的`loadClass`方法将的字节码文件加载到内存中。加载器根据的全限定名查找文件,并将其转换为一个`Class`对象。该阶段的核心任务是获取的二进制数据,并在内存中创建对应的`Class`对象。 2. **验证(Verification)** 验证阶段确保的字节码是合法的,并且不会对JVM造成危害。该阶段主要检查字节码的结构是否符合规范,例如检查的魔数、版本号、常量池等。验证是加载过程中最复杂的阶段之一,它防止恶意代码或错误编写的文件破坏JVM的稳定性。 3. **准备(Preparation)** 准备阶段为的静态变量分配内存,并设置默认初始值。例如,`int`型的静态变量会被初始化为0,`boolean`型会被初始化为`false`。需要注意的是,该阶段不执行的构造方法,仅分配内存并设置初始值。 4. **解析(Resolution)** 解析阶段将、接口、字段和方法的符号引用转换为直接引用。符号引用是以字符串形式表示的或方法的名称,而直接引用则是指向内存地址的指针。解析可以在初始化之前或之后进行,具体取决于JVM实现。 5. **初始化(Initialization)** 初始化阶段执行的构造方法`<clinit>`,该方法是由编译器自动收集中的所有静态变量赋值动作和静态代码块合并生成的。此阶段是加载过程的最后一步,也是真正开始使用之前的最后一个准备步骤。 6. **使用(Using)** 在使用阶段,程序可以通过`Class`对象创建的实例,并调用其方法。此时,已经完全加载并初始化,可以正常参与程序的运行。 7. **卸载(Unloading)** 卸载阶段是加载过程的最后一个阶段,主要涉及垃圾回收机制。当不再被使用且没有实例存在时,JVM可以卸载该并回收其占用的内存资源。 #### 加载JVM中通过加载器`ClassLoader`的`loadClass`方法对进行装载。加载器分为以下几种: 1. **引导加载器(Bootstrap ClassLoader)** 引导加载器负责加载JVM核心库,如`java.lang.String`等。这些通常位于`lib`目录下,由JVM自身管理。 2. **扩展加载器(Extension ClassLoader)** 扩展加载器负责加载JVM的扩展库,如`javax.crypto`包中的。这些通常位于`lib/ext`目录下。 3. **应用程序加载器(Application ClassLoader)** 应用程序加载器负责加载用户自定义的,这些通常位于项目的`classpath`中。 以下是一个简单的示例代码,用于测试所使用的具体加载器: ```java /** * 加载器过程 */ public class TestJDKClassLoader { public static void main(String[] args) { // 核心程序 支撑JVM运行 位于lib目录下 引导加载器 System.out.println("引导加载器:" + String.class.getClassLoader()); // 扩展程序 支撑JVM运行 位于lib目录中ext下的包 扩展加载器 System.out.println("扩展加载器:" + DESKeyFactory.class.getClassLoader()); // 自己编写的程序 运行自身业务流程 应用程序加载器 System.out.println("应用程序加载器:" + TestJDKClassLoader.class.getClassLoader()); } } ``` #### 加载顺序 加载的开始顺序是确定的,即加载、验证、准备、初始化、卸载的顺序是固定的。然而,这些阶段的结束顺序并不一定严格按照开始顺序进行。解析阶段可能在初始化之后开始,具体取决于JVM实现。此外,加载无需等到程序中“首次使用”的时候才开始,JVM可以预先加载某些以提高性能。
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值