1 从JDK源码级别剖析JVM类加载机制

本文详细解释了Java类加载的懒加载机制,重点介绍了双亲委派原理以及如何通过自定义类加载器实现对特定类的加载,同时强调了双亲委派的沙箱安全和避免重复加载的作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

1 执行Main方法的过程

2 类加载过程

3 类加载器和双亲委派机制

3.1 双亲委派核心源码

3.2 自定义类加载器


1 执行Main方法的过程

2 类加载过程

        java类加载属于懒加载,用到才加载

证明:

public class TestDynamicLoad {
    static {
        System.out.println("*************load TestDynamicLoad************");
    }

    public static void main(String[] args) {
        new A();
        System.out.println("*************load test************");
        B b = null; //B不会加载,除非这里执行 new B()
    }

}

class A {
    static {
        System.out.println("*************load A************");
    }

    public A() {
        System.out.println("*************initial A************");
    }
}

class B {
    static {
        System.out.println("*************load B************");
    }

    public B() {
        System.out.println("*************initial B************");
    }
}

执行结果:

步骤:

        1 执行main方法,需要加载TestDynamicLoad类(执行静态代码块)

        2 new A()时,加载A类,先执行类中的静态代码块 -> 构造方法

        3 B没使用,不会加载

 

3 类加载器和双亲委派机制

引导类加载器:jre/lib;支撑JVM运行的核心类库

扩展类加载器:jre/lib/ext;扩展jar包

应用程序类加载器:java.class.path;自己写的类

自定义加载器:用户自定义路径下的类包

示例:

System.out.println(String.class.getClassLoader());
System.out.println(com.sun.crypto.provider.DESKeyFactory.class.getClassLoader().getClass().getName());
System.out.println(TestJDKClassLoader.class.getClassLoader().getClass().getName());

结果:

示例:

ClassLoader appClassLoader = ClassLoader.getSystemClassLoader();
ClassLoader extClassloader = appClassLoader.getParent();
ClassLoader bootstrapLoader = extClassloader.getParent();
System.out.println("the bootstrapLoader : " + bootstrapLoader);
System.out.println("the extClassloader : " + extClassloader);
System.out.println("the appClassLoader : " + appClassLoader);

结果:

        层级:app < ext < bootstrap

3.1 双亲委派核心源码

 protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            //若之前被加载,则直接获取
            Class<?> c = findLoadedClass(name);      
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    if (parent != null) {
                        //当前parent=extClassLoader
                        c = parent.loadClass(name, false);   
                    } else {
                        //extClassLoader.parent=null(bootStrapClassLoader)
                        c = findBootstrapClassOrNull(name);  
                    }
                } catch (ClassNotFoundException e) {
                }

                if (c == null) {
                    long t1 = System.nanoTime();

                    //都会调用URLClassLoader的findClass方法在加载器的类路径里查找并加载该类 -> 执行defineClass方法实现类加载过程
                    c = findClass(name);
                    ...
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }

为什么要有双亲委派?

        沙箱安全机制:防止核心类被加载

        避免重复加载:如果类被加载过一次,则不需要重复加载(findLoadedClass)

3.2 自定义类加载器

public class Liuxin {

    static class LiuxinClassLoader extends ClassLoader {

        private String classPath;

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

        @Override
        protected Class<?> findClass(String name) throws ClassNotFoundException {
            byte[] data = new byte[0];
            try {
                data = loadByte(name);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return defineClass(name, data, 0, data.length);
        }

        protected Class<?> loadClass(String name, boolean resolve)
                throws ClassNotFoundException {
            synchronized (getClassLoadingLock(name)) {
                Class<?> c = findLoadedClass(name);
                if (c == null) {
                    if (!name.startsWith("User")) {
                        c = this.getParent().loadClass(name);
                    } else
                        c = findClass(name);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
                if (resolve) {
                    resolveClass(c);
                }
                return c;
            }
        }

        private byte[] loadByte(String name) throws Exception {
            name = name.replaceAll("\\.", "/");
            FileInputStream fis = new FileInputStream(classPath + "/" + name + ".class");
            int len = fis.available();
            byte[] data = new byte[len];
            fis.read(data);
            fis.close();
            return data;
        }

        public static void main(String args[]) throws Exception {
            //初始化自定义类加载器,会先初始化父类ClassLoader,其中会把自定义类加载器的父加载器设置为应用程序类加载器AppClassLoader
            LiuxinClassLoader classLoader = new LiuxinClassLoader("D:\\DeskTop");
            //D盘创建 test/com/tuling/jvm 几级目录,将User类的复制类User1.class丢入该目录
            Class clazz = classLoader.loadClass("User");
            Object obj = clazz.newInstance();
            Method method = clazz.getDeclaredMethod("sout", null);
            method.invoke(obj, null);
            System.out.println(clazz.getClassLoader().getClass().getName());

            LiuxinClassLoader classLoader1 = new LiuxinClassLoader("D:\\DeskTop\\liuxin");
            Class clazz1 = classLoader.loadClass("User");
            Object obj1 = clazz.newInstance();
            Method method1 = clazz.getDeclaredMethod("sout", null);
            method.invoke(obj, null);
            System.out.println(clazz.getClassLoader().getClass().getName());
        }
    }
}

 使用:

public static void main(String args[]) throws Exception {
    //初始化自定义类加载器,会先初始化父类ClassLoader,其中会把自定义类加载器的父加载器设置为应用程序类加载器AppClassLoader
    LiuxinClassLoader classLoader = new LiuxinClassLoader("D:/test");
    //D盘创建 com/liuxin/User1 几级目录,将User类的复制类User1.class丢入该目录
    Class clazz = classLoader.loadClass("com.liuxin.User1");
    Object obj = clazz.newInstance();
    Method method = clazz.getDeclaredMethod("sout", null);
    method.invoke(obj, null);
    System.out.println(clazz.getClassLoader().getClass().getName());
}

         自定义类加载器的父加载器为:appClassLoader

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值