第一步: 给你一个编译好的class文件以及它的包名,创建一个对象出来。
1)class文件源代码
package com.wsc.classloader; public class Tool{ public void print() { } }
2)使用javac Tool.java 编译成class文件
3)将Tool.class文件读取到内存中,生成byte[]数组
/** * 加载class文件 * * @param clazzPath * class绝对文件路径 * @return 字节数组 * @throws IOException */ private byte[] loadClassFile(String clazzPath) throws IOException { FileInputStream fis = new FileInputStream(clazzPath); BufferedInputStream bis = new BufferedInputStream(fis); ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte[] buffer = new byte[1024 * 256]; int ch = 0; while ((ch = bis.read(buffer, 0, buffer.length)) != -1) { baos.write(buffer, 0, ch); } return baos.toByteArray(); }
4)自定义ClassLoader,使用ClassLoader中的defineClass方法:protected final Class<?> defineClass(String name, byte[] b, int off, int len)。参数分别是类名称,class文件对应的字节数组,起始位置和终止位置。
@Override protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { Class<?> c = findLoadedClass(name); if (c == null) { c = defineClass(name, data, 0, data.length); } return c; }
整体代码是:
package com.wsc.classloader; import java.io.BufferedInputStream; import java.io.ByteArrayOutputStream; import java.io.FileInputStream; import java.io.IOException; public class ClassLoaderOne extends ClassLoader { public static void main(String[] args) throws Exception { ClassLoaderOne loader = new ClassLoaderOne( "E:\\JAVA\\JAVAFX\\ClassLoader\\libs\\Tool.class"); Class<?> clazz = loader.loadClass("com.wsc.classloader.Tool"); Object o = clazz.newInstance(); System.out.println(o.getClass().getClassLoader()); } private byte[] data; public ClassLoaderOne(String clazzPath) throws IOException { data = loadClassFile(clazzPath); } /** * 加载class文件 * * @param clazzPath * class绝对文件路径 * @return 字节数组 * @throws IOException */ private byte[] loadClassFile(String clazzPath) throws IOException { FileInputStream fis = new FileInputStream(clazzPath); BufferedInputStream bis = new BufferedInputStream(fis); ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte[] buffer = new byte[1024 * 256]; int ch = 0; while ((ch = bis.read(buffer, 0, buffer.length)) != -1) { baos.write(buffer, 0, ch); } return baos.toByteArray(); } @Override protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { Class<?> c = findLoadedClass(name); if (c == null) { c = defineClass(name, data, 0, data.length); } return c; } }
感觉是这样的,跑一下:
Exception in thread "main" java.lang.SecurityException: Prohibited package name: java.lang at java.lang.ClassLoader.preDefineClass(Unknown Source) at java.lang.ClassLoader.defineClass(Unknown Source) at java.lang.ClassLoader.defineClass(Unknown Source) at com.wsc.classloader.ClassLoaderOne.loadClass(ClassLoaderOne.java:52) at java.lang.ClassLoader.loadClass(Unknown Source) at java.lang.ClassLoader.defineClass1(Native Method) at java.lang.ClassLoader.defineClass(Unknown Source) at java.lang.ClassLoader.defineClass(Unknown Source) at com.wsc.classloader.ClassLoaderOne.loadClass(ClassLoaderOne.java:52) at java.lang.ClassLoader.loadClass(Unknown Source) at com.wsc.classloader.ClassLoaderOne.main(ClassLoaderOne.java:14)
意思是:禁止加载名为java.lang的包。
原因是:虽然Tool类中没有使用任何引入java.lang下类,但是它的父类Object是在java.lang下的,classloader加载Tool类时会把它所有的关系网都加载出来才行,父类Object肯定是要加载的。
这样就简单了!无非多写一个If else.使用父加载器(类加载器都有一个父类加载器)加载即可。
@Override protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { Class<?> c = findLoadedClass(name); if (name.equals("java.lang.Object")) { ClassLoader parent = getParent(); c = parent.loadClass(name); } if (c == null) { c = defineClass(name, data, 0, data.length); } return c; }
跑一下结果是:
com.wsc.classloader.ClassLoaderOne@ca470
第二步:新的问题
Method[] methods = clazz.getMethods(); for (int i = 0; i < methods.length; i++) { String name = methods[i].getName(); System.out.println(name); Class<?>[] params = methods[i].getParameterTypes(); for (int j = 0; j < params.length; j++) { System.out.println(params[j].toString()); } }
这个时候还是会报刚才的错误,因为Method类也在java.lang包下,只能在增加一个If else.
显然,代码应该这样写
@Override protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { Class<?> c = findLoadedClass(name); if (c == null) { // 如果父加载器不为null,使用父类加载器加载(比如Object,HashMap等核心类) if (getParent() != null) { try { c = getParent().loadClass(name); } catch (Exception e) { // 父类可能没加载,则抛异常 } } // 如果父类加载器没有加载,再使用自定义加载器加载 if (c == null) { c = defineClass(name, data, 0, data.length); } } return c; }
打印结果:
toString print class java.lang.String getClass hashCode equals class java.lang.Object notify notifyAll wait long int wait wait long com.wsc.classloader.ClassLoaderOne@fcfa52
第三步:如果本地可以通过.class文件创建,远程当然也已同一个道理(如果需要加密,在本地多一个解密即可)。如果class文件是远程调用的话,本地一般使用接口或者反射两种方法调用。首选是接口,反射一是效率,而是要清楚所有的方法名称、参数名称过于麻烦。
由于远程加载class文件到本地,如果出错很难定位出错位置。幸好,classloader使用规则默认是根据URLClassLoader来使用的,会先根据检查本地是否有该类,所以可以直接将源码放在本地即可调试,当然发布的时候一定要删除。
如图:
通过这个基本的入门程序可以了解ClassLoader的基本流程。
相关推荐
在示例代码中,我们创建了一个名为`Test`的类,它读取`HelloWorld.class`文件,然后遍历常量池,找到索引23的`ConstantUtf8Info`对象,并将其值更改为"Hello World HELLO WORLD"。修改完成后,使用`ClassFileWriter`...
1. **ClassPool**: `ClassPool` 类是 Javassist 的核心组件,它管理着一系列的 `CtClass` 对象,这些对象代表了 `.class` 文件。`getDefault()` 方法会返回一个默认的 `ClassPool`,它会从当前的类加载器中获取 `....
在本主题中,我们将探讨如何使用Python来解析Java的Class文件,并对其进行简单的执行。这是一项有趣的技术,可以帮助我们理解Java字节码的工作原理,同时也可以在Python环境中运行Java代码。 首先,Java的Class文件...
例如,可以使用`FileStream`类创建一个新的.dat文件,然后通过`BinaryWriter`或`StreamWriter`写入数据。以下是一个基本示例: ```csharp using System.IO; // 创建一个.dat文件 string filePath = "data.dat"; ...
1. **创建对象**:在ASP中,你可以通过`new`关键字实例化一个`jsonObject`对象,如`set json = new jsonObject`。 2. **添加键值对**:`add`方法用于向JSON对象添加键值对,例如`json.add "key", "value"`。 3. **...
标题“java编译class文件”涉及的核心知识点是Java的编译过程。Java源代码文件的扩展名为`.java`,这些文件包含类定义、方法定义以及其他Java语法元素。要将`.java`文件编译成`.class`文件,我们需要使用Java的JDK...
我们可以使用class literal来获取类的元数据,如类名、接口实现、父类等,也可以用于创建类的新实例(通过`newInstance()`方法,前提是该类有无参构造器)。 接着,我们讨论 "instance.getClass()"。这个方法是每个...
3. **创建Statement或PreparedStatement对象**:根据需求选择合适的对象类型,如需动态参数则使用PreparedStatement。 4. **执行SQL语句**:通过Statement或PreparedStatement的executeQuery()或executeUpdate()...
- 创建新的工作簿:使用`PHPExcel`类实例化一个新的Excel对象。 - 添加工作表:通过`addSheet()`方法向工作簿添加新的工作表。 - 写入单元格数据:使用`setCellValue()`或`setCellValueExplicit()`方法写入单元格...
Java虚拟机(JVM)是Java程序运行的基础,它负责解析和执行编译后的.class文件。这个过程涉及多个阶段,包括加载、验证、准备、解析和初始化。在本篇文章中,我们将深入探讨JVM如何处理.class文件,以及相关工具如何...
通过类,我们可以创建对象,这些对象是类的实例,拥有类定义的属性和行为。 在压缩包的文件名称列表中,有一个名为"db.class.php"的文件。这通常包含定义"my db class"的代码,即作者自定义的数据库类。在这个文件...
这时,可以使用反编译工具(如JAD)将Class文件转换回源代码形式,便于理解和调试。 通过理解和掌握这些知识点,开发者能更好地理解和使用Core Java,从而编写出高效、稳定的Java应用程序。无论是初学者还是经验...
在Java编程中,动态代理是一种强大的技术,它允许我们在运行时创建对象的代理,以便在调用实际方法之前或之后执行额外的操作。本压缩包文件提供了两个示例项目,分别展示了JDK动态代理和CGLib动态代理的实现,帮助...
那么,如果要在程序退出之前能动态的实时 加载修改后编译出来的新的.class文件中的效果,则需要创建动态对象,如同Tomcat中的热部署效果一样。 于是,专门创建动态对象的工具包DynaObject产生了。 DynaObject...
换言之,每当编写并且编译了一个新类,就会产生一个Class对象(恰当地说,是被保存在一个同名的.class文件中)。在运行时,当我们想生成这个类的对象时,运行这个程序的 Java虚拟机(JVM)首先检查这个类的Class对象...
如果已经加载,则根据`.class`文件创建实例对象。 2. **单例性**:通过不同的方式生成的`Class`对象实际上是指向同一个`Class`实例。这意味着即使使用不同的方法获取`Class`对象,也只会存在一个`Class`实例。 3. ...
开发者可以通过创建PreparedStatement对象来执行预编译的SQL语句,或者通过Statement对象来执行基本的SQL命令。 描述中的".zip"和".rar"都是常见的文件压缩格式,它们用于将多个文件或目录打包成一个单一的文件,...
4. **动态创建对象**:有了类名后,我们可以使用`QMetaObject::newInstance`来动态创建对象。这个函数接受一个类型ID或类名字符串作为参数,返回一个`QObject`指针。如果类没有继承自`QObject`,则无法使用此方法。 ...
通过反射创建对象时,可以使用Class类的newInstance()方法。但是需要注意的是,这要求Customer类必须有一个默认构造方法,且该方法需要无参。使用反射创建对象的代码示例如下: ```java Class objClass = Class.for...
它接受类路径字符串和类名,创建`URL`对象,然后使用自定义类加载器加载类并实例化。 - 实例化后,可以通过类型转换将加载的类强制转换为接口类型,然后调用接口方法。 5. **环境变量与类路径**: - 通常,Java...