java类加载机制的深入分析

jvm类加载机制在很多书籍里面都有讲解,主要介绍了基于委托的类的加载机制、类加载链接初始化的过程、以及类加载时机等,本篇文章通过编写自定义的classloader来对相关概念进行更加具体直观的分析。

源码样例分析

SelfClassLoader.java

package cloader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

/**
 * 自定义的类加载器
 * Start at: 2018/3/27 23:53
 *
 * @author xx
 */
public class SelfClassLoader extends ClassLoader {

    private final String CLASS_SUFFIX = ".class";

    // 该加载器默认加载class的路径
    private String classPath;

    public SelfClassLoader(String classPath){
        this.classPath = classPath;
    }

    @Override
    public Class<?> loadClass(String name) throws ClassNotFoundException {
        /*
         * name是全限定名,需要确保那么classPath下面有全限定名对应的文件夹
         * 如果name:com.mh.RemoteHello,那么classPath下面必须有对应的文件路径
         * classPath+com/mh/RemoteHello.class
         */
        System.out.println("==============Start to load class("+name+")=============");
        if (null == classPath || null == name){
            throw new ClassNotFoundException("Classpath or name is null:"+classPath
                    +",name:"+name);
        }
        // getParent():Launcher的AppClassLoader(应用加载器)
        // getParent().getParent():Launcher的ExtClassLoader(系统扩展加载器)
        ClassLoader extClassLoader = getParent().getParent();
        Class<?> cls = null;
        try{
            // 首先交由系统加载器加载,如果系统加载器抛出了class not found
            // 那么由自定义加载器进行加载
            cls = extClassLoader.loadClass(name);
        }catch (ClassNotFoundException e){
            System.out.println("ExtClassLoader can not load class");
        }
        /*
         * 如果jvm系统的加载器不能加载,那么子类继续加载
         * 因为如果通过该类加载器加载应用类,那么应用类所引用的所有的类(包括系统类)
         * 都会通过该类加载,对于系统类来说首先交由系统加载器加载,但是系统加载器无法
         * 加载用户自己写的类,所以这样的类需要用户自己加载
         */
        if(cls != null){
            System.out.println("Parent load class success:"+name);
            return cls;
        }
        System.out.println("Self classloader load class success:"+name);
        cls = findClass(name);
        System.out.println("==============End to load class("+name+")=============");
        return cls;
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        String pathName = name.replace("." ,"\\");
        String classFilePath = classPath + pathName + CLASS_SUFFIX;
        Class<?> cls = null;
        try {
            byte[] classBytes = getClassFileBytes(classFilePath);
            // 从字节数组生成class对象
            cls = defineClass(name, classBytes, 0, classBytes.length);
        } catch (Exception e) {
            throw new ClassNotFoundException(e.getMessage());
        }
        return cls;
    }

    /**
     * 读取class文件的字节数组
     * @param classFilePath class文件的绝对路径
     * @return 字节数组
     * @throws Exception 文件不存在或者是读取字节数组失败
     */
    private byte[] getClassFileBytes(String classFilePath) throws Exception{
        File f = new File(classFilePath);
        if (!f.exists()) {
            throw new FileNotFoundException("Can not find file");
        }
        FileChannel channel = null;
        FileInputStream fs = null;
        try {
            fs = new FileInputStream(f);
            channel = fs.getChannel();
            ByteBuffer byteBuffer = ByteBuffer.allocate((int) channel.size());
            while ((channel.read(byteBuffer)) > 0) {
            }
            return byteBuffer.array();
        } catch (IOException e) {
            throw e;
        } finally {
            try {
                if(channel != null){
                    channel.close();
                }
                if(fs != null){
                    fs.close();
                }
            } catch (IOException e) {
                throw e;
            }
        }
    }
}

TestMain.java

package cloader;

import org.junit.Test;

import java.lang.reflect.Method;

/**
 * 测试类加载机制
 * Start at: 2018/3/27 23:53
 *
 * @author xx
 */
public class TestMain {


    @Test
    public void test(){
        // 需要加载的class文件所在的路径
        String classPath = "D:\\tmp\\";
        ClassLoader classLoader = new SelfClassLoader(classPath);
        // 需要加载的class文件的名称
        // 如果包含了package信息,那么需要把整个目录拷贝到上面的路径下
        String className = "RemoteHello";
        try {
            Class cls = classLoader.loadClass(className);
            Object target = cls.newInstance();
            Method method = cls.getMethod("sayHello");
            method.invoke(target);
        } catch (ClassNotFoundException e) {
            System.out.println(e.getMessage());
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

RemoteHello.java

/**
 * 测试类
 * Start at: 2018/3/27 23:54
 *
 * @author muhong
 */
public class RemoteHello {

    public void sayHello(){
        System.out.println("Hello world!");
    }
}

运行结果分析

==============Start to load class(RemoteHello)=============
ExtClassLoader can not load class
Self classloader load class success:RemoteHello
==============Start to load class(java.lang.Object)=============
Parent load class success:java.lang.Object
==============End to load class(RemoteHello)=============
==============Start to load class(java.lang.System)=============
Parent load class success:java.lang.System
说明了被该加载器加载的类中的其他类的引用都是通过该加载器加载,但是如果该引用的类
是系统类,那么该类由系统加载器加载
==============Start to load class(java.io.PrintStream)=============
Parent load class success:java.io.PrintStream
Hello world!
阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页