java反射机制(3)- 动态类加载与重载(Load and Reload)

本文详细介绍了Java中的类加载器、层次结构,以及动态类加载和重载的概念。类加载器按照Bootstrap Loader -> ExtClassLoader -> AppClassLoader的委托模型工作。动态类加载可以通过类加载器的loadClass()方法实现,而动态类重载则需要自定义ClassLoader子类,并解决类的唯一标识问题。在设计类重载时,建议使用接口或超类来避免类型转换错误。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1 类加载器 The ClassLoader

  Java中的所有类,必须被装载到jvm中才能运行,这个装载工作是由JVM中的类装载器Class Loader完成的,类装载器所做的工作实质是把类文件从硬盘读取到内存中 。
  当一个类完成加载时,所涉及的类也都会被加载。类加载过程是一个递归的模式。但并不一定是一个应用里面所有类都会被加载,与这个被加载类的引用链无关的类是不会被加载的,直到有引用关系的时候它们才会被加载。比如函数中涉及的类,只有在函数被调用时才会加载。

2 类加载器的层次结构 The ClassLoader Hierarchy

  java中的类大致分为三种:
1. 系统类
2. 扩展类
3. 由程序员自定义的类
  
  类加载方式,有两种 :
1. 隐式加载, 程序在运行过程中当碰到通过new 方式生成对象时,隐式调用类装载器加载对应的类到jvm中;
2. 显式加载, 通过class.forname()等方法,显式加载需要的类 。即反射机制。


   JVM中类加载器的层次结构如下:
   Bootstrap Loader - 负责加载系统类
     |
     - - ExtClassLoader - 负责加载扩展类
      |
       - - AppClassLoader - 负责加载应用类

   java采用了委托模型机制,这个机制简单来讲,就是“类装载器有载入类的需求时,会先请示其Parent使用其搜索路径帮忙载入,如果Parent 找不到,那么才由自己依照自己的搜索路径搜索类”。类加载是一个Hierarchy递归的过程。类加载器加载类的顺序如下:
  
1. 检查这个类是否已经被加载。
2. 如果没有被加载,则首先调用父加载器加载。
3. 如果父加载器不能加载这个类,则尝试调用该类的加载器加载这个类。

当你实现一个有重载类功能的类加载器,它的顺序与上述会有些不同。类重载不会请求的他的父加载器来进行加载。在后面的段落会进行讲解。

3 动态类加载 Dynamic Class Loading

  动态加载一个类很简单。要做的就是获取该类的加载器ClassLoader然后调用它的loadClass()方法。

package com.markliu.reflection.classloader;

public class ClassloaderTest {

    public static void main(String[] args) {
        ClassLoader classLoader = ClassloaderTest.class.getClassLoader();
        try {
            Class<?> aClass = classLoader.loadClass("com.markliu.reflection.classloader.ClassloaderTest");
            System.out.println("aClass.getName() = " + aClass.getName());
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

4 动态类重载 Dynamic Class Reloading

  动态类重载有一定难度。Java内置的类加载器在加载一个类之前会检查它是否已经被加载。因此重载一个类是无法使用Java内置的类加载器的。为了重新加载一个类,需要自己实现ClassLoader的子类。
  在实现了ClassLoader的子类之后,还有一些事需要做。所有被加载的类都需要被链接( Every loaded class needs to be linked. )。链接的过程是通过ClassLoader.resolve()方法来完成的。由于这是一个final方法,因此在ClassLoader的子类中无法被重写。resolve()方法是不允许给定的ClassLoader实例链接一个类两次。所以每当你想要重载一个类的时候你都需要使用一个新的ClassLoader的子类。这个问题并不是不可实现的,而且是在设计类重载是必须要解决的。下面给出方案。

5 自定义类重载 Designing your Code for Class Reloading

  在前面已经说过不能使用已经加载过类的类加载器来重载一个类。因此你需要一个其他不同的ClassLoader实例来重载这个类。但是这又带来了一些新的问题。
  所有被加载到Java应用中的类都以类的全名(包名 + 类名)作为一个唯一标识来让ClassLoader实例来加载。这意味着,类MyObject被类加载器A加载获得的Class,和被类加载器B加载获得的Class类是不同的类!看看下面的代码:

MyObject object = (MyObject)myClassReloadingFactory.newInstance("com.jenkov.MyObject");

  MyObject类在上面那段代码中被引用,它的变量名是object。这就会导致MyObject类会被这段代码所在类的类加载器所加载。就是说,如果这段代码在OtherObject中,MyObject会被加载OtherObject的类加载器所加载。
  如果myClassReloadingFactory工厂对象使用和加载上述代码所在类的加载器不同的类加载器重载MyObject类,你不能把重载的MyObject类的实例转换(cast)到类型为MyObject的对象变量。一旦MyObject类分别被两个类加载器加载,那么它就会被认为是两个不同的类,尽管它们的类的全名是完全一样的。你如果尝试把这两个类的实例进行转换就会报ClassCastException
  解决方案,需要从以下两个方面入手:
1、标记这个变量类型为一个接口,然后只重载这个接口的实现类。
2、标记这个变量类型为一个超类,然后只重载这个超类的子类。

请看下面这两个例子:

MyObjectInterface object = (MyObjectInterface)
myClassReloadingFactory.newInstance("com.jenkov.MyObject");
MyObjectSuperclass object = (MyObjectSuperclass)
myClassReloadingFactory.newInstance("com.jenkov.MyObject");

  只要保证变量的类型是超类或者接口,这两个方法就可以正常运行,当它们的子类或是实现类被重载的时候超类跟接口是不会被重载的。
  为了保证这种方式可以运行,你需要手动实现类加载器然后使得这些接口或超类可以被它的父加载器加载。当你的类加载器加载MyObject类时,超类MyObjectSuperclass或者接口MyObjectSuperclass也会被加载,因为它们是MyObject的依赖。你的类加载器必须要代理这些类的加载到同一个类加载器,这个类加载器加载这个包括接口或者超类的类。

6 类加载器加载和重载示例ClassLoader Load / Reload Example

  下面这个例子是一个类加载器的子类。注意在这个类不想被重载的情况下它是如何把对一个类的加载代理到它的父加载器上的。如果一个类被它的父加载器加载,这个类以后将不能被重载。记住,一个类只能被同一个ClassLoader实例加载一次。
  就像之前说的那样,这仅仅是一个简单的例子,通过这个例子会向你展示类加载器的基本行为。这并不是一个可以让你直接用于设计你项目中类加载器的模板。你自己设计的类加载器应该不仅仅只有一个,如果你想用来重载类的话你可能会设计很多加载器。并且你也不会像下面这样将需要加载的类的路径硬编码(hardcore)到你的代码中。

package com.markliu.reflection.classloader;

public class Snippet {
    public static void main(String[] args) throws Exception {

        ClassLoader parentClassLoader = MyObjectClassLoader.class.getClassLoader();
        System.out.println("parentClassLoader:" + parentClassLoader);
        MyObjectClassLoader classLoader = new MyObjectClassLoader(parentClassLoader);
        System.out.println("classLoader:" + classLoader);

        Class<?> myObjectClass = classLoader.loadClass("com.markliu.reflection.classloader.MyObject");

        AnInterface2 object1 = (AnInterface2) myObjectClass.newInstance();
        MyObjectSuperClass object2 = (MyObjectSuperClass) myObjectClass.newInstance();

        // 创建新的类加载器,所以类可以重新加载
        classLoader = new MyObjectClassLoader(parentClassLoader);
        myObjectClass = classLoader.loadClass("com.markliu.reflection.classloader.MyObject");

        object1 = (AnInterface2) myObjectClass.newInstance();
        object2 = (MyObjectSuperClass) myObjectClass.newInstance();
        System.out.println("object1:" + object1);
        System.out.println("object2:" + object2);
    }
}

  MyObject的自定义类加载器:

package com.markliu.reflection.classloader;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;

/**
 * com.markliu.reflection.classloader.MyObject的自定义类加载器
 */
public class MyObjectClassLoader extends ClassLoader{

    public MyObjectClassLoader(ClassLoader parent) {
        super(parent);
    }

    public Class<?> loadClass(String name) throws ClassNotFoundException {
        if(!"com.markliu.reflection.classloader.MyObject".equals(name))
                return super.loadClass(name);

        try {
            File file = new File("/media/markliu/Entertainment/Linux/SoftwareInformation/java/My_javase_projects/reflection反射机制/bin/com/markliu/reflection/classloader/MyObject.class");
            InputStream input = new FileInputStream(file);
            ByteArrayOutputStream buffer = new ByteArrayOutputStream();
            int data;

            while((data = input.read())!= -1){
                buffer.write(data);
            }
            input.close();

            byte[] classData = buffer.toByteArray();

            return defineClass("com.markliu.reflection.classloader.MyObject",
                    classData, 0, classData.length);

        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
}

  MyObject类:

public class MyObject extends MyObjectSuperClass implements AnInterface2 {
    // some other codes
}

原文链接:Java Reflection - Dynamic Class Loading and Reloading
中文参考:并发编程网 – ifeve.com

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Sunny Mornings

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值