JVM类加载机制(二)[亲测有效]

JVM类加载机制(二)[亲测有效]之前在JVM类加载机制(一)中介绍过类加载机制的基本原理,今天从代码角度来展示下从磁盘上加载一个类字节码的效果。

欢迎大家来到IT世界,在知识的湖畔探索吧!

之前在JVM类加载机制(一)中介绍过类加载机制的基本原理,今天从代码角度来展示下从磁盘上加载一个类字节码的效果。

准备一个简单的类,目标是从磁盘上加载这个类的字节码,然后调用test()方法:

public class HelloWorld {
    private static int i = 3;
    static {
        System.out.println("init");
    }
    public static void main(String[] args) {
        System.out.println("Hello World");
    }
    public void test(){
        System.out.println("test");
    }
}

欢迎大家来到IT世界,在知识的湖畔探索吧!

通过下面的命令将HelloWorld.java编译成HelloWorld.class:

欢迎大家来到IT世界,在知识的湖畔探索吧!javac HelloWorld.java 
# output
HelloWorld.class 

编译好的HelloWorld.class其实就是一个二进制的字节码文件。
可以从javap命令来进行反编译:

javap HelloWorld.class
欢迎大家来到IT世界,在知识的湖畔探索吧!Compiled from "HelloWorld.java"
public class HelloWorld {
  public HelloWorld();
  public static void main(java.lang.String[]);
  public void test();
  static {};
}

也可以通过sublime软件打开,如下:

JVM类加载机制(二)[亲测有效]

我们从磁盘上加载这个字节码文件,首先读取字节流:

public static byte[] readClassByte(String filePath) throws IOException {
        FileInputStream inputStream = new FileInputStream(filePath);
        long fileSize = new File(filePath).length();
        byte[] allBytes = new byte[(int)fileSize];
        inputStream.read(allBytes);
        return allBytes;
}

这个方法参数filePath就是HelloWorld.class文件的路径,读取的字节流存放在byte[]类型的数组中,然后通过来defineClass方法加载字节码:

  byte[] classBytes = readClassByte("/Users/pro/Desktop/Helloworld.class");
  ClassLoader classLoader = ClassLoader.getSystemClassLoader();
  Method method = Class.forName("java.lang.ClassLoader")
  .getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
  method.setAccessible(true);
  Class clazz = (Class) method.invoke(classLoader, classBytes, 0, classBytes.length);

由于defineClass方法是protected类型的,因此除子类之外的其他外部类无法直接调用这个方法,所以这里采用反射的方式来调用它,主要是为了展示类加载的效果,最后返回clazz对象。
这里的这个clazz对象实际上就是HelloWorld类,然后下一步就是调用HelloWorld类中的方法,继续使用反射:

Method method = clazz.getDeclaredMethod("test");
method.invoke(clazz.newInstance());

最后就可以看到输出的结果:

init
test

可以看到除了输出test结果之外,还有init,这个是来自于HelloWorld类中静态代码块的执行,如下:

static {
     System.out.println("init");
}

根据类加载的上一篇JVM类加载机制(一)可以看到上面的过程主要模拟了一个类加载、链接、初始化的过程,而这个init就是在初始化的时候输出的。
这里的类加载过程比较明显,但是链接过程被隐藏了,如果要显示展示链接过程,可以加上如下的代码:

 Method method = Class.forName("java.lang.ClassLoader")
                .getDeclaredMethod("resolveClass", Class.class);
 method.setAccessible(true);
 method.invoke(classLoader,clazz);

这里的resolveClass方法就是用来对类做链接的,与defineClass方法一样,都是protected类型的,所以也通过反射调用。

完整的代码如下:

import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class CustomLoadClass {
    public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, NoSuchFieldException, InstantiationException {
        //类加载
        byte[] classBytes = readClassByte("<二进制类文件的路径>");
        ClassLoader classLoader = ClassLoader.getSystemClassLoader();
        Method method = Class.forName("java.lang.ClassLoader")
                .getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
        method.setAccessible(true);
        Class clazz = (Class) method.invoke(classLoader, classBytes, 0, classBytes.length);

        //类链接
        method = Class.forName("java.lang.ClassLoader")
                .getDeclaredMethod("resolveClass", Class.class);
        method.setAccessible(true);
        method.invoke(classLoader,clazz);

        //类调用(包含初始化)
        method = clazz.getDeclaredMethod("test");
        method.invoke(clazz.newInstance());
    }

    public static byte[] readClassByte(String filePath) throws IOException {
        FileInputStream inputStream = new FileInputStream(filePath);
        long fileSize = new File(filePath).length();
        byte[] allBytes = new byte[(int) fileSize];
        inputStream.read(allBytes);
        return allBytes;
    }
}

以上主要通过模拟一个二进制类加载、链接到执行的过程来理解类加载概念上的原理,如果要在一个产品中去自定义加载一些类或者jar包,主要是通过自定义类加载器的方式来做,下一篇JVM类加载文章会进行介绍。

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://itzsg.com/17707.html

(0)

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

联系我们YX

mu99908888

在线咨询: 微信交谈

邮件:itzsgw@126.com

工作时间:时刻准备着!

关注微信