浅谈JAVA 类加载器

2020-06-22 18:02 来源:易采站长站 作者:于海丽 点击: 评论:

A-A+

原标题:浅谈JAVA 类加载器

类加载机制

类加载器负责加载所有的类,系统为所有被载入内存中的类生成一个 java.lang.Class 实例。一旦一个类被载入 JVM 中,同个类就不会被再次载入了。现在的问题是,怎么样才算“同一个类”?

正如一个对象有一个唯一的标识一样,一个载入 JVM 中的类也有一个唯一的标识。在 Java 中,一个类用其全限定类名(包括包名和类名)作为标识:但在 JVM 中,一个类用其全限定类名和其类加载器作为唯一标识。例如,如果在 pg 的包中有一个名为 Person 的类,被类加载器 ClassLoader 的实例 k1 负责加载,则该 Person 类对应的 Class 对象在 JVM 中表示为(Person、pg、k1)。这意味着两个类加载器加载的同名类:(Person、pg、k1)和(Person、pg、k12)是不同的,它们所加载的类也是完全不同、互不兼容的。

当 JVM 启动时,会形成由三个类加载器组成的初始类加载器层次结构。

Bootstrap ClassLoader:根类加载器。 Extension ClassLoader:扩展类加载器。 System ClassLoader:系统类加载器。

Bootstrap ClassLoader 被称为引导(也称为原始或根)类加载器,它负责加载 Java 的核心类。在Sun 的 JVM 中,当执行 java.exe 命令时,使用 -Xbootclasspath 或 -D 选项指定 sun.boot.class.path 系统属性值可以指定加载附加的类。

JVM的类加载机制主要有如下三种。

全盘负责。所谓全盘负责,就是当一个类加载器负责加载某个 Class 时,该 Class 所依赖的和引用的其他 Class 也将由该类加载器负责载入,除非显式使用另外一个类加载器来载入。 父类委托。所谓父类委托,则是先让 parent(父)类加载器试图加载该 Class,只有在父类加载器无法加载该类时才尝试从自己的类路径中加载该类。 缓存机制。缓存机制将会保证所有加载过的 Class 都会被缓存,当程序中需要使用某个 Class 时,类加载器先从缓存区中搜寻该 Class,只有当缓存区中不存在该 Class 对象时,系统才会读取该类对应的二进制数据,并将其转换成 Class 对象,存入缓存区中。这就是为什么修改了 Class 后,必须重新启动 JVM,程序所做的修改才会生效的原因。

除了可以使用 Java 提供的类加载器之外,开发者也可以实现自己的类加载器,自定义的类加载器通过继承 ClassLoader 来实现。JVM 中这4种类加载器的层次结构如下图所示。

注意:类加载器之间的父子关系并不是类继承上的父子关系,这里的父子关系是类加载器实例之间的关系

下面程序示范了访问 JVM 的类加载器。

public class ClassLoaderPropTest {
 public static void main(String[] args) throws IOException {
 // 获取系统类加载器
 ClassLoader systemLoader = ClassLoader.getSystemClassLoader();
 System.out.println("系统类加载器:" + systemLoader);
 /*
  * 获取系统类加载器的加载路径——通常由CLASSPATH环境变量指定 如果操作系统没有指定CLASSPATH环境变量,默认以当前路径作为
  * 系统类加载器的加载路径
  */
 Enumeration<URL> em1 = systemLoader.getResources("");
 while (em1.hasMoreElements()) {
  System.out.println(em1.nextElement());
 }
 // 获取系统类加载器的父类加载器:得到扩展类加载器
 ClassLoader extensionLader = systemLoader.getParent();
 System.out.println("扩展类加载器:" + extensionLader);
 System.out.println("扩展类加载器的加载路径:" + System.getProperty("java.ext.dirs"));
 System.out.println("扩展类加载器的parent: " + extensionLader.getParent());
 }
}

【易采站长站编辑:秋军】