Java 自定义 ClassLoader 实现 JVM 类加载

原文: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.

你可能感兴趣的:(Java 自定义 ClassLoader 实现 JVM 类加载)