原文:https://mp.weixin.qq.com/s/RaFIPhG1y1kmhue--MD-EQ
定义需要加载的类
定义类加载器
编译需要加载的类文件
编译自定义的类加载器并执行程序
总结
定义需要加载的类
为了能够实现类加载,并展示效果,定义一个Hello类,再为其定义一个sayHello()方法,加载Hello类之后,调用它的sayHello()方法。
public class Hello {
public static void sayHello(){
System.out.println("Hello,I am ....");
}
}
定义类加载器
自定义加载器,需要继承ClassLoader,并重写里面的findClass(String name) 方法。
public class MyClassLoader extends ClassLoader {
/**
* 重写父类方法,返回一个Class对象
* ClassLoader中对于这个方法的注释是:
* This method should be overridden by class loader implementations
*/
protected Class<?> findClass(String name) throws ClassNotFoundException {
Class clazz = null;
String classFilename = name + ".class";
File classFile = new File(classFilename);
if (classFile.exists()) {
try (FileChannel fileChannel = new FileInputStream(classFile)
.getChannel();) {
MappedByteBuffer mappedByteBuffer = fileChannel
.map(MapMode.READ_ONLY, 0, fileChannel.size());
byte[] b = mappedByteBuffer.array();
clazz = defineClass(name, b, 0, b.length);
} catch (IOException e) {
e.printStackTrace();
}
}
if (clazz == null) {
throw new ClassNotFoundException(name);
}
return clazz;
}
public static void main(String[] args) throws Exception{
MyClassLoader myClassLoader = new MyClassLoader();
Class clazz = myClassLoader.loadClass(args[0]);
Method sayHello = clazz.getMethod("sayHello");
sayHello.invoke(null, null);
}
}
编译需要加载的类文件
类加载的时候加载的是字节码文件,所以需要预先把定义的Hello类编译成字节友文件。
javac Hello.java
编译自定义的类加载器并执行程序
编译代码
javac MyClassLoader.java
当然我们也可以同时编译我们所有的java源文件
javac *.java
执行成功之后,我们用下面的语句执行代码,测试是否成功,并查看结果
java MyClassLoader Hello
运行结果
Hello,I am ....
当程序按照预期显示,就证明我自定义类加载器成功了。
总结
通过上面的程序代码,简单的实现JVM的类加载过程,知道了程序运行的一点流程。但是在编写的时候有如下坑需要注意:
类文件不需要指定包,否则加载的时候我们需要额外的处理,把包中的"."替换成文件系统的路径"/"。
需要加载的Hello类中的反射调用的方法要用static修饰,这样invoke的时候第一个参数才可以使用null关键字代替,否则需要创建一个对应的类实例。
官方文档中有这样一句话If the underlying method is static, then the specified obj argument is ignored. It may be null.