class文件的动态加载

在我们实际开发中经常会遇到一些问题,比如某些类我们需要动态的加载进java虚拟机的内存区域。
     要实现这个功能我们就需要了解java虚拟机的几个类加载器。
     Java 中的类加载器大致可以分成两类,一类是系统提供的,另外一类则是由 Java 应用开发人员编写的。系统提供的类加载器主要有下面三个:
     引导类加载器(bootstrap class loader):它用来加载 Java 的核心库,是用原生代码来实现的,并不继承自 java.lang.ClassLoader。
     扩展类加载器(extensions class loader):它用来加载 Java 的扩展库。Java 虚拟机的实现会提供一个扩展库目录。该类加载器在此目录里面查找并加载 Java 类。
     系统类加载器(system class loader):它根据 Java 应用的类路径(CLASSPATH)来加载 Java 类。一般来说,Java 应用的类都是由它来完成加载的。可以通过 ClassLoader.getSystemClassLoader() 来获取它。
    除了系统提供的类加载器以外,开发人员可以通过继承 java.lang.ClassLoader 类的方式实现自己的类加载器,以满足一些特殊的需求。
    我们平时程序执行的时候在类加载器中寻找类的结构的顺序是:引导类加载器-》 扩展类加载器-》系统类加载器-》我们自定义的一些类加载器,每个类加载器都有自己的空间,同一个加载器里面的类的二进制名字必须是唯一的,当然同一个类也可以存在不同的加载器内存区域里面,不过我们寻找类的时候是按顺序找的,一但找的也就不会继续往下找了,最终也没找到就会报类不存在异常。
    我们如果想动态加载类的话就要仿照我们用的服务器如tomcat和weblogic之类的,他们的开发模式也就是把所有的类都加载到自身的类加载器中,当文件被替换的时候他们就重新加载新的class到内存里面去,从而实现了类的动态加载。
    以下是个人是实现一个动态加载类的小程序:
    Class A 是需要动态加载的一个小程序:

package jvm;

public class A {
public static void doSome(){
  System.out.println("测试类的动态加载!");
}
}

      我把A.class文件放到D盘,然后通过我自己写的类来动态加载他:

package jvm;

import java.io.File;
import java.io.FileInputStream;
import java.lang.reflect.Method;

public class LoaderSomeClass {
public static void main(String[] args) throws Exception {
ClassLoaderSub classLoaderSub = new ClassLoaderSub();
Class clazz = classLoaderSub.getClassByFile("D:/A.class");
System.out.println("类的名字:"+clazz.getName());
System.out.println("类的加载器:"+clazz.getClassLoader());
classLoaderSub.loadClass(A.class.getName());
Object obj = clazz.newInstance();
Method method = clazz.getMethod("doSome");
method.invoke(obj, null);
}
}

class ClassLoaderSub extends ClassLoader{
public  Class defineClassByName(String name,byte[] b,int off,int len){
  //由于defineClass是protected,所以需要继承后来调用
  Class clazz = super.defineClass(name,b,off,len);
  return clazz;
}

public Class getClassByFile(String fileName) throws Exception{
  File classFile = new File(fileName);
  //一般的class文件通常都小于100k,如果现实情况超出这个范围可以放大长度
  byte bytes []= new byte[102400];
  FileInputStream fis = null;
  Class clazz = null;
  try {
   fis = new FileInputStream(classFile);
   int j = 0;
   while (true) {
    int i = fis.read(bytes);
    if (i == -1)
     break;
    j += i;
   }
   clazz = defineClassByName(null,bytes,0,j);
  } finally{
   fis.close();
   return clazz;
  }
}
}
输出结果为:
类的名字:jvm.A
类的加载器:jvm.ClassLoaderSub@1fb8ee3
测试类的动态加载!
<think>我们被要求回答关于Java动态加载class文件的方法,并参考了提供的三个引用。用户的问题是如何在Java中实现动态加载class文件的方法或机制。 根据引用[1]和引用[2],我们可以总结出两种主要方法: 1. 使用URLClassLoader从文件系统或JAR文件加载类。 2. 使用Class.forName()方法,但这个方法通常用于加载已经在classpath中的类,不过可以通过指定类加载器来加载不在classpath中的类。 另外,引用[3]提到了在Springboot环境中动态加载class文件需要考虑自定义类加载器以及处理嵌套JAR的问题。 因此,我们可以将动态加载class文件的方法归纳为: 方法1:使用URLClassLoader 步骤: a. 创建一个File对象指向包含class文件的目录或JAR文件。 b. 将File对象转换为URL对象。 c. 创建URLClassLoader,传入URL数组(可以包含多个URL)。 d. 使用URLClassLoader的loadClass方法加载指定类名的类(全限定名)。 e. 通过反射创建实例或调用方法。 方法2:使用Class.forName()并指定类加载器 步骤: a. 同样需要先创建一个类加载器(如URLClassLoader)来加载指定路径的类。 b. 使用Class.forName(String name, boolean initialize, ClassLoader loader)方法,其中第三个参数就是指定的类加载器。 注意:直接使用Class.forName()(不指定类加载器)只能加载classpath下的类,而指定类加载器后,就可以通过自定义的类加载加载任意路径的类。 另外,在Springboot等框架中,由于使用了自定义的类加载器(如LaunchedURLClassLoader),可能需要特别处理依赖问题,比如重写JavaFileManager来使用springboot的jarFile读取嵌套jar。 下面我们按照要求,用中文回答,结构清晰,并生成相关问题。</think>### Java 动态加载 Class 文件的方法 在 Java 中实现动态加载 class 文件主要有以下两种核心方法,均基于类加载机制实现: --- #### **方法 1:使用 `URLClassLoader`(推荐)** 适用于加载**外部目录或 JAR 包**中的 class 文件: ```java // 1. 创建指向 class 文件目录/JAR 的 File 对象 File file = new File("E:/lib/my_classes.jar"); // 2. 转换为 URL 对象 URL url = file.toURI().toURL(); // 3. 创建 URLClassLoader 并指定加载路径 URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{url}); // 4. 动态加载指定类(全限定名) Class<?> clazz = urlClassLoader.loadClass("com.example.MyClass"); // 5. 创建实例 Object instance = clazz.getDeclaredConstructor().newInstance(); ``` **特点**: - 可加载任意路径的 class 文件(包括网络资源)[^1] - 独立于默认 classpath,避免污染应用类路径 - 支持 JAR/ZIP 压缩包的直接加载 --- #### **方法 2:使用 `Class.forName()` 指定类加载器** 需配合自定义类加载器使用: ```java // 创建自定义 ClassLoader(如 URLClassLoader) URLClassLoader loader = new URLClassLoader(...); // 动态加载类(不初始化静态块) Class<?> clazz = Class.forName("com.example.MyClass", false, loader); // 创建实例 Object instance = clazz.newInstance(); ``` **特点**: - 通过 `false` 参数可延迟静态初始化 - 需显式传递类加载器对象[^2] - 本质上仍依赖底层类加载器实现 --- #### **关键注意事项** 1. **类隔离** 不同 `ClassLoader` 加载的相同类会被 JVM 视为不同类,需避免类型转换异常。 2. **依赖问题** 动态加载的类若依赖其他类,需确保: - 依赖库在自定义类加载器的搜索路径中 - 或通过 `Parent-Delegate` 模型委托父加载器处理 3. **Spring Boot 特殊处理** 在 Spring Boot 的嵌套 JAR 环境中,需重写 `JavaFileManager` 处理依赖加载[^3]。 4. **热部署限制** 同一个 `ClassLoader` 无法重复加载同名的类,需通过销毁旧加载器实现热替换。 --- **
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值