龙空技术网

详细介绍一下Java中的类加载过程?

从程序员到架构师 298

前言:

现时咱们对“java加载类的三种方式”大体比较关注,咱们都需要知道一些“java加载类的三种方式”的相关资讯。那么小编在网摘上网罗了一些对于“java加载类的三种方式””的相关内容,希望同学们能喜欢,朋友们快快来学习一下吧!

在Java中类的加载过程是Java虚拟机将类的字节码加载到内存中、进行验证、然后解析和初始化的过程。在这个过程中涉及多个步骤和执行阶段。Java的类加载机制是动态的,即类在运行时才会被加载。这种机制为Java的高灵活性和跨平台特性提供了支持。下面我们就来详细的介绍一下相关的内容。

类加载器 (Class Loader)

Java提供了三种主要的类加载器来动态加载类,如下所示。

Bootstrap ClassLoader(引导类加载器):负责加载JVM核心类库,例如rt.jar中的类(如java.lang.*等)。它是由C/C++代码实现的,处于JVM内部。Extension ClassLoader(扩展类加载器): 负责加载Java扩展库,例如$JAVA_HOME/lib/ext/目录中的类。Application ClassLoader(应用类加载器): 负责加载应用程序的类路径CLASSPATH中的类。

Java的类加载器遵循双亲委托机制,即当一个类加载器尝试加载类时,它首先将请求委托给父类加载器,如果父类加载器无法加载该类,才由当前类加载器进行加载。

类加载的生命周期

类的加载生命周期主要包括以下五个阶段

加载(Loading)

类的字节码文件(.class文件)通过类加载器从文件系统、网络或者其他来源加载到内存中。在此过程中,JVM会为每一个加载的类在方法区(Method Area)中生成一个Class对象,它封装了该类的信息,例如类的名称、父类、实现的接口、字段、方法等。

链接(Linking)

链接过程由三个小步骤组成

验证(Verification): 验证加载的字节码是否符合JVM的规范,以确保字节码不会破坏JVM的安全性。例如,确保类文件格式合法,数据类型符合规范,父类和子类的兼容性等。准备(Preparation): 为类的静态字段分配内存并初始化为默认值。例如,如果有static int x,那么会为x分配内存并赋值为默认值0。解析(Resolution): 将类、字段、方法的符号引用(符号化的名称)解析为直接引用(内存地址)。符号引用指的是类文件中的抽象符号,而直接引用是类加载器所生成的可以被直接使用的实体。

初始化(Initialization)

初始化阶段是执行类构造器方法的过程。类构造器是由编译器自动生成的,用于初始化类的静态变量和执行静态代码块。如果类存在静态字段,或者有静态代码块,它们会在此阶段执行,并对字段赋初始值。初始化方法只执行一次,是线程安全的。

使用(Using)

当类被加载并初始化后,它的实例可以被创建,类的静态方法可以被调用,静态字段可以被访问。

卸载(Unloading)

类的卸载是由垃圾回收器负责的。当某个类的Class对象不再被引用时,并且这个类的所有实例都被回收,JVM会在适当的时候卸载这个类。类的卸载是无法强制触发的,通常由JVM自动进行。

类加载器的双亲委托模型

Java的类加载器使用了父委托机制,即当一个类加载器收到类加载请求时,它会首先将该请求委托给父加载器处理。父类加载器处理的顺序是从最顶层的Bootstrap ClassLoader开始,依次向下。只有当父类加载器无法找到类时,才会由当前类加载器进行加载。

双亲委托机制的优势

避免类的重复加载: 如果某个类已经被父类加载器加载,那么子类加载器不需要再次加载。保证核心类库的安全性: 由于核心类库由引导类加载器加载,应用程序无法伪造和替换核心类库中的类。

虽然Java类加载器遵循父委托模型,但在一些情况下,开发者可能会打破这种机制,通常通过创建自定义类加载器,继承自ClassLoader类并覆盖loadClass()方法。这种做法在一些框架,例如OSGi、Tomcat等中比较常见,它们通过定制类加载器来隔离不同的类加载上下文。

类的主动引用与被动引用

类的加载有两种类型,如下所示。

主动引用

主动引用会导致类的加载和初始化,包括以下情况:

使用new关键字创建类的实例。调用类的静态方法或者访问类的静态变量。使用反射来访问类。初始化某个类的子类时,会先初始化父类。JVM启动时指定的主类。

被动引用

某些情况不会触发类的初始化,即不会调用初始化方法方法:

通过子类访问父类的静态字段,只会初始化父类,而不会初始化子类。定义数组时不会初始化数组元素的类,例如MyClass[] array = new MyClass[10];不会初始化MyClass。访问final常量(编译期常量),因为这些常量在编译期已被嵌入调用类的常量池中。总结

Java的类加载过程是一个复杂而关键的过程,包含了加载、链接、初始化等多个阶段,并依赖于类加载器及其父委托模型来确保类的唯一性和安全性。理解类加载机制有助于开发者在处理类加载异常、ClassNotFoundException、NoClassDefFoundError等问题时更好地进行调试和解决问题。

标签: #java加载类的三种方式