LZ说的这个文件,当然不仅仅指的是jar包中的class文件,而是硬盘上任意位置的class文件。
LZ首先想到的是Class的forName方法,该方法传入类的全名(xx.xx.xx.xx),返回该类的Class类的实例。然后再通过newInstance方法创建该类的实例:
1 2 3 |
Class<Example> clazz = (Class<Example>) Class.forName("pkg.Example"); Example example = clazz.newInstance(); |
但是,用这种方法加载class文件,该class文件必须在classpath下才可以。
1 2 3 4 5 6 7 |
@CallerSensitive public static Class<?> forName(String className) throws ClassNotFoundException { Class<?> caller = Reflection.getCallerClass(); return forName0(className, true, ClassLoader.getClassLoader(caller), caller); } |
看源码可知,forName是通过ClassLoader来加载的,所以LZ认为其中一定可以有加载任意class文件的方法。
1 2 3 4 5 6 |
protected final Class<?> defineClass(String name, byte[] b, int off, int len) throws ClassFormatError { return defineClass(name, b, off, len, null); } |
这个方法通过传入class文件的字节数字,可以返回Class类的实例。但是这个方法是protected的,所以要自己实现一个子类.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
public class MyClassLoader extends ClassLoader { public Class<?> loadMyClass() { Class<?> clazz = null; String myClass = "/Users/zhengyi/Desktop/pkg/Example.class"; File classFile = new File(myClass); if (classFile.exists()) { try (FileChannel fileChannel = new FileInputStream(classFile).getChannel()) { ByteBuffer byteBuffer = ByteBuffer.allocate((int) fileChannel.size()); while ((fileChannel.read(byteBuffer)) > 0) { } byte[] b = byteBuffer.array(); clazz = defineClass("Example", b, 0, b.length); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } return clazz; } } |
Example.java的内容:
1 2 3 4 5 6 |
public class Example { public void sayHello() { System.out.println("Hello, My ClassLoader"); } } |
然后就可以用自定义的这个加载器加载这个类了。
1 2 3 4 5 |
MyClassLoader loader = new MyClassLoader(); Class<?> example = loader.loadMyClass(); Method sayHello = example.getDeclaredMethod("sayHello"); sayHello.invoke(example.newInstance()); |
事实上,不用自己定义一个新方法,只要重写findClass方法就可以。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
public class MyClassLoader extends ClassLoader { @Override protected Class<?> findClass(String name) throws ClassNotFoundException { Class<?> clazz = null; String myClass = "/Users/zhengyi/Desktop/pkg/" + name + ".class"; File classFile = new File(myClass); if (classFile.exists()) { try (FileChannel fileChannel = new FileInputStream(classFile).getChannel()) { ByteBuffer byteBuffer = ByteBuffer.allocate((int) fileChannel.size()); while ((fileChannel.read(byteBuffer)) > 0) { } byte[] b = byteBuffer.array(); clazz = defineClass(name, b, 0, b.length); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } return clazz; } } |
加载时候使用loadClass方法来加载类。
1 2 3 4 5 |
MyClassLoader loader = new MyClassLoader(); Class<?> example = loader.loadClass("Example"); Method sayHello = example.getDeclaredMethod("sayHello"); sayHello.invoke(example.newInstance()); |
使用loadClass方法的好处是,它会帮你自动完成双亲委派的操作。
99 Replies to “如何加载一个class文件”