Java反射 - 动态类加载和重载
可以使用Java在运行时加载和重新加载类,虽然它不像人们希望的那样简单。 本文将解释何时以及如何在Java中加载和重新加载类。
ClassLoader
Java应用程序中的所有类都使用java.lang.ClassLoader的一些子类加载。 因此,动态加载类也必须使用java.lang.ClassLoader子类来完成。
当一个类被加载时,它所引用的所有类也被加载。 这个类加载模式是递归的,直到所有需要的类都被加载。 这可能不是应用程序中的所有类。 未引用的类只有在引用时才加载。
装载等级
Java中的类加载器被组织成一个层次结构。 当你创建一个新的标准的Java ClassLoader时,你必须提供一个父类的ClassLoader。 如果一个ClassLoader被要求加载一个类,它会要求它的父类加载器加载它。 如果父类加载器找不到该类,那么子类加载器将尝试自己加载它。
类加载
给定类加载器在加载类时使用的步骤如下:
- 检查类是否已经加载。
- 如果未加载,则请求父类加载器加载该类。
- 如果父类加载器无法加载类,请尝试将其加载到此类加载器中。
当你实现一个能够重新加载类的类加载器时,你将需要偏离这个序列。 父类加载器不应请求加载的类加载。详细情况请看Java的类加载机制(ClassLoader)
动态类加载
动态加载类很容易。 所有你需要做的就是获得一个ClassLoader并且调用它的loadClass()方法。 这里是一个例子:
public class MainClass {public static void main(String[] args){ClassLoader classLoader = MainClass.class.getClassLoader();try {Class aClass = classLoader.loadClass("com.jenkov.MyClass");System.out.println("aClass.getName() = " + aClass.getName());} catch (ClassNotFoundException e) {e.printStackTrace();}}
动态类重载
动态类重新加载有点更具挑战性。 Java的内置Class加载器在加载之前总是检查一个类是否已经被加载。 因此,使用Java的内置类加载器不能重新加载类。 重新加载一个类,你将不得不实现你自己的ClassLoader子类。
即使使用ClassLoader的自定义子类,也是一个挑战。 每个加载的类都需要链接。 这是使用ClassLoader.resolve()方法完成的。 这个方法是final的,因此不能在你的ClassLoader子类中重写。 resolve()方法将不允许任何给定的ClassLoader实例连接两次相同的类。 因此,每次你想重新加载一个类,你都必须使用ClassLoader子类的一个新实例。 这不是不可能的,但是在设计类重新加载时需要知道。
设计类重新加载
如前所述,您不能使用已经加载该类的ClassLoader重载一个类。 因此,您将不得不使用不同的ClassLoader实例重新加载类。 但是这带来了很多新的挑战。
加载到Java应用程序中的每个类都由其完全限定的名称(包名称+类名称)和加载它的ClassLoader实例标识。 这意味着,类加载器A加载的类MyObject与加载类加载器B的MyObject类不是同一个类。请看下面的代码:
MyObject object = (MyObject)myClassReloadingFactory.newInstance("com.jenkov.MyObject");
注意代码中MyObject类是如何作为对象变量的类型引用的。这会导致MyObject类被加载了该代码所在的类的同一个类加载器加载。
如果myClassReloadingFactory对象工厂使用与上面的代码所在的类不同的类加载器重新加载MyObject类,则不能将重新加载的MyObject类的实例转换为对象变量的MyObject类型。由于两个MyObject类用不同的类加载器加载,因此即使它们具有相同的完全限定类名,它们也被视为不同的类。尝试将一个类的对象转换为另一个类的引用将导致ClassCastException。
有可能解决此限制,但您将不得不以两种方式之一更改您的代码:
- 使用一个接口作为变量类型,并重新加载实现类。
- 使用父类作为变量类型,并重新加载一个子类。
这是两个相应的代码示例:
MyObjectInterface object =(MyObjectInterface)myClassReloadingFactory.newInstance("com.jenkov.MyObject");
MyObjectSuperclass object = (MyObjectSuperclass)myClassReloadingFactory.newInstance("com.jenkov.MyObject");
如果在重载实现类或子类时变量的类型(接口或超类)没有重载,则这两种方法都可以工作。
为了使这个工作,你当然需要实现你的类加载器,让父类加载接口或父类。 当你的类加载器被要求加载MyObject类时,它也会被要求加载MyObjectInterface类或者MyObjectSuperclass类,因为这些类是从MyObject类中引用的。 您的类加载器必须将这些类的加载委托给加载包含接口或超类型变量的类的相同类加载器。
类加载器加载/重载的例子
上面的文字包含了很多的话题。 我们来看一个简单的例子。 下面是一个简单的ClassLoader子类的例子。 注意它是如何将类加载委派给它的父类,除了它可以重新加载的类之外。 如果将此类的加载委托给父类加载器,则以后无法重新加载。 请记住,一个类只能由相同的ClassLoader实例加载一次。
如前所述,这只是一个示例,用于向您展示ClassLoader行为的基础知识。你自己的类加载器可能不应该局限于一个类,而是你需要重新加载的类的集合。 另外,你也许不应该对类路径进行硬编码。
public class MyClassLoader extends ClassLoader{public MyClassLoader(ClassLoader parent) {super(parent);}public Class loadClass(String name) throws ClassNotFoundException {if(!"reflection.MyObject".equals(name))return super.loadClass(name);try {String url = "file:C:/data/projects/tutorials/web/WEB-INF/" +"classes/reflection/MyObject.class";URL myUrl = new URL(url);URLConnection connection = myUrl.openConnection();InputStream input = connection.getInputStream();ByteArrayOutputStream buffer = new ByteArrayOutputStream();int data = input.read();while(data != -1){buffer.write(data);data = input.read();}input.close();byte[] classData = buffer.toByteArray();return defineClass("reflection.MyObject",classData, 0, classData.length);} catch (MalformedURLException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}return null;}}
下面是一个示例使用MyClassLoader。
public static void main(String[] args) throwsClassNotFoundException,IllegalAccessException,InstantiationException {ClassLoader parentClassLoader = MyClassLoader.class.getClassLoader();MyClassLoader classLoader = new MyClassLoader(parentClassLoader);Class myObjectClass = classLoader.loadClass("reflection.MyObject");AnInterface2 object1 =(AnInterface2) myObjectClass.newInstance();MyObjectSuperClass object2 =(MyObjectSuperClass) myObjectClass.newInstance();//创建新的类加载以至于能被重载classLoader = new MyClassLoader(parentClassLoader);myObjectClass = classLoader.loadClass("reflection.MyObject");object1 = (AnInterface2) myObjectClass.newInstance();object2 = (MyObjectSuperClass) myObjectClass.newInstance();}
这是使用类加载器加载的reflection.MyObject类。 注意它是如何扩展一个超类并实现一个接口的。 这仅仅是为了这个例子。 在你自己的代码中,你只需要其中的一个 - 扩展或实现。
public class MyObject extends MyObjectSuperClass implements AnInterface2{//... 覆盖父类方法// 或者实现接口方法
}
Java反射 - 动态类加载和重载相关推荐
- java 反射 动态代理
在上一篇文章中介绍Java注解的时候,多次提到了Java的反射API.与javax.lang.model不同的是,通过反射API可以获取程序在运行时刻的内部结构.反射API中提供的动态代理也是非常强大 ...
- 深入理解Java反射+动态代理,java开发面试笔试题
我总结出了很多互联网公司的面试题及答案,并整理成了文档,以及各种学习的进阶学习资料,免费分享给大家. 扫描二维码加VX好友,拉你进[程序员面试学习交流群]免费领取.也欢迎各位一起在群里探讨技术. 答: ...
- 【java】深入分析Java反射-动态代理 proxy
1.概述 转载:深入分析Java反射(四)-动态代理 [Java]Java 反射机制浅析 [Java]java代理 静态代理 动态代理 proxy [java]静态代理 proxy 2.动态代理的简介 ...
- java 反射 动态_java实现反射,动态配置
标签://java 实现反射 //首先定义一个接口Ishow.java public interface Ishow(){ void show(String str); } //建立一个demo.ja ...
- Java反射与类加载过程会擦出什么样的火花
反射与类加载 一,反射机制原理 二,反射机制中的问题解答 三,反射中的常用API 四, 类加载过程详解 一,反射机制原理 反射机制介绍 1.反射机制允许程序在执行期借助于ReflectionAPI取得 ...
- java反射-动态代理
转自:https://www.cnblogs.com/throwable/p/12272269.html 动态代理的简介# Java动态代理机制的出现,使得Java开发人员不用手工编写代理类,只要简单 ...
- java反射 动态调用_java反射拼接方法名动态执行方法
近期由于负责项目的一个模块,该模块下有很多分类,每个分类都有一个编码code,这个值是作为一个参数携带过来的.但是每个code确实对应一个方法的. code的值有很多个,自己又不想做ifelse或者s ...
- java 反射动态修改数据类型_[Java-基础]反射_Class对象_动态操作
动态性 动态语言 在程序运行时,可以改变程序结构或变量类型,典型的语言: Python,ruby,javascript 如: function test(){ var s = "var a= ...
- java 反射 动态编译_动态编译java源代码和反射调用问题
我从教程中得到了以下代码: package com.tom.labs; import java.io.IOException; import java.lang.reflect.Method; imp ...
最新文章
- SharePoint 2016 必备组件离线安装介绍
- mysql5.5异常: java.sql.SQLException: Packet for query is too large (1055975 1048576).
- 每天一个linux命令目录
- 使用Lombok优化代码
- 了解 Boost Filesystem Library文件系统
- PHP源码分析-PHP的生命周期
- R语言心得-分词包的安装
- C/C++中extern关键字详解[zz]
- css --- [读书笔记] 浮动(float) 与 清除浮动
- 嵌套For循环性能优化
- mysql undrop_MySQL 如何对InnoDB使用Undrop来恢复InnoDB数据
- 节 海里/小时 千米/小时 米/秒 转换
- HDU 1251 - 统计难题(字典树模板题)
- 10. Browser 对象 - Location 对象(2)
- 【论文解读】VDN( Variational Denoising Network )变分去噪网络
- PLC可编程控制器实验装置
- MES生产制造执行系统与APS计划排产系统相关名词解释
- 两阶段随机规划模型简介
- c语言高级编程培训,C语言高级编程
- linux DRM/KMS 测试工具 modetest、kmscude、igt-gpu-tools (一)