栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

类加载器的类型

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

类加载器的类型

类的加载阶段将类的.class文件中的二进制数据读入到内存中,将其常量池、属性表、方法表、异常表放在运行时数据区的方法区内,然后在创建一个对应的java.lang.Class对象,用来封装类在方法区内的数据结构
JVM主要在程序第一次主动使用类的时候,才会去加载该类,而且只加载一次

jvm支持两种类型的加载器,分别是引导类加载器(启动类加载器)和自定义加载器,引导类加载器是由c/c++实现的,自定义加载器是由java实现的。自定义加载器派生于ClassLoader,它包括扩展类加载器(Extension Class Loader),系统类加载器(System Class Loader),自定义加载器(User-Defined ClassLoader)

引导类加载器(启动类加载器)

使用c/c++实现,嵌套再jvm内部
它用来加载Java的核心类库(JAVA_HOME/jre/lib/rt.jar、 resource.jar或sun.boot.class.path路径下的内容),用于提供JVM自身需要的类。
并不继承自java.lang.ClassLoader,没有父加载器

扩展类加载器

java语言编写,由sun.misc.Launcher$ExtClassLoader实现 (ExtClassLoader是ClassLoader的子类)
从java.ext.dirs系统属性所指定的目录中加载类库,或从JDK的安装目录的jre/lib/ext 子目录(扩展目录)下加载类库。如果用户创建的JAR 放在此目录下,也会自动由扩展类加载器加载。
父类加载器为启动类加载器

系统类加载器

java语言编写,由 sun.misc.Lanucher$AppClassLoader 实现(AppClassLoader 是ClassLoader的子类)
该类加载是程序中默认的类加载器,一般来说Java应用的类都是由它来完成加载的,它负责加载环境变量classpath或系统属性java.class.path 指定路径下的类库
父类加载器为扩展类加载器
通过 ClassLoader#getSystemClassLoader() 方法可以获取到该类加载器。

用户自定义类加载器

在日常的Java开发中,类加载几乎是由三种加载器配合执行的,在必要时我们还可以自定义类加载器,来定制类的加载方式。
自定义加载器的作用:

1.隔离加载类
把类加载到不同的应用系统中。比如tomcat这类web应用服务器,内部自定义了好几中类加载器,用于隔离web应用服务器上的不同应用程序。
2.修改类加载方式
除了Bootstrap加载器外,其他的加载器并非一定要引入。根据实际情况在某个时间点按需进行动态加载。
3.扩展加载源
比如还可以从数据库、网络、或其他终端上加载
4.防止源码泄漏
java代码容易被编译和篡改,可以进行编译加密,类加载需要自定义还原加密字节码。

public class GetClassLoader {
    public static void main(String[] args) {
        //获取系统类加载器
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
        System.out.println(systemClassLoader);

        //扩展类加载器
        ClassLoader extClassLoader = systemClassLoader.getParent();
        System.out.println(extClassLoader);

        //获取自定义类加载器
        ClassLoader classLoader = GetClassLoader.class.getClassLoader();
        System.out.println(classLoader);

        //同样的方式获取不到启动类加载器
        ClassLoader bootStrapClassLoader = extClassLoader.getParent();
        System.out.println(bootStrapClassLoader);
    }
}

双亲委派

如果一个类加载器收到类加载的请求,它自己先不会去加载这个类,它会先把这个加载的请求委派给父类加载器加载。如果父类加载器在自己的范围内加载不到(ClassNotFoundException),自己才回尝试去加载。

自定义了一个java.lang.String并启动

package java.lang;

public class String {
    public static void main(String[] args) {
        System.out.println("123");
    }
}

父类会先加载java自己的java.lang.String,然后这里使用String的类方法的时候,使用的就是父类的加载的java.lang.String

在JVM中,判断一个对象是否是某个类型时,如果该对象的实际类型与待比较的类型的类加载器不同,那么会返回false。

双亲委派的实现原理
进入java.lang.ClassLoader

    public Class loadClass(String name) throws ClassNotFoundException {
        return loadClass(name, false);
    }
    
    protected Class loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // First, check if the class has already been loaded
            //检查是否加载过这个class
            Class c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    if (parent != null) {
                    	//先让父加载器加载
                        c = parent.loadClass(name, false);
                    } else {
                    	//如果父加载器为null则用引导类加载器加载
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }
				//如果父类加载器无法加载
                if (c == null) {
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    long t1 = System.nanoTime();
                    //尝试自己加载,这个方法需要classloader的子类自己实现
                    c = findClass(name);

                    // this is the defining class loader; record the stats
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }
    protected Class findClass(String name) throws ClassNotFoundException {
        throw new ClassNotFoundException(name);
    }

自定义类加载器的实现
1继承ClassLoader类
2重写loadClass方法或者重写findClass方法
loadClass是实现双亲委派机制逻辑的地方,修改他需要重写双亲委派机制的逻辑,推荐重写findClass方法

创建TestClass

public class TestClass {
}

在文件路径下编译文件

javac TestClass.java 

public class MyClassLoader extends ClassLoader {
    private String codePath;

    public MyClassLoader(String codePath) {
        this.codePath = codePath;
    }

    @Override
    protected Class findClass(String name) {
        BufferedInputStream bis = null;
        ByteArrayOutputStream baos = null;
        try {
            //1.字节码路径
            String fileName = codePath + name + ".class";
            //2.获取输入流
            bis = new BufferedInputStream(new FileInputStream(fileName));
            //3.获取输出流
            baos = new ByteArrayOutputStream();
            //4.io读写
            int len;
            byte[] data = new byte[1024];
            while ((len = bis.read(data)) != -1) {
                baos.write(data, 0, len);
            }
            //5.获取内存中字节数组
            byte[] byteCode = baos.toByteArray();
            //6.调用defineClass 将字节数组转成Class对象
            Class defineClass = defineClass(null, byteCode, 0, byteCode.length);
            return defineClass;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                bis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                baos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    public static void main(String[] args) {
    	//这里写入自己的TestClass.class路径
        MyClassLoader classLoader = new MyClassLoader("TestClass.class的路径");
        try {
            Class clazz = classLoader.loadClass("TestClass");
            System.out.println("TestClass.class的类加载器是" + clazz.getClassLoader().getClass().getName());
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/584455.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号