Java常用类:ClassLoader

作为一个稀有的Java妹子,所写的所有博客都只是当作自己的笔记,留下证据自己之前是有用心学习的~哈哈哈哈(如果有不对的地方,也请大家指出,不要悄悄咪咪的不告诉我)

ClassLoader

1.ClassLoader的作用

Class Loader是用来把.class字节码文件加载到jvm内存中的,也就是把.class文件加载为Class对象。

2.ClassLoader的分类

Java内置了三个重要的ClassLoader,分别是 BootstrapClassLoader、ExtensionClassLoader 和 AppClassLoader。

BootstrapClassLoader 负责加载 JVM 运行时核心类,这些类位于 JAVA_HOME/lib/rt.jar 文件中,我们常用内置库 java.xxx.* 都在里面,比如 java.util.、java.io.、java.nio.、java.lang. 等等。这个 ClassLoader 比较特殊,它是由 C 代码实现的,我们将它称之为「根加载器」。

ExtensionClassLoader 负责加载 JVM 扩展类,比如 swing 系列、内置的 js 引擎、xml 解析器 等等,这些库名通常以 javax 开头,它们的 jar 包位于 JAVA_HOME/lib/ext/*.jar 中,有很多 jar 包。

AppClassLoader 才是直接面向我们用户的加载器,它会加载 Classpath 环境变量里定义的路径中的 jar 包和目录。我们自己编写的代码以及使用的第三方 jar 包通常都是由它来加载的。

3.双亲委派模型

当类加载器接受到加载类的请求时,首先是查看当前类是否已经加载,未加载的话查找是否有父级类加载器,有的话把加载类的操作交给父类加载器来完成,最终加载操作会到BootstrapClassLoader,如果BootstrapClassLoader不能加载这类,则让ExtensionClassLoader加载,如果ExtensionClassLoader也不能加载,就让AppClassLoader加载。
这样加载的好处就是使得每个类只被加载一次,且重要的基础类,如String,如果用户自己定义一个String,加载后就会破坏程序,而双亲委派模型保证String只会被BootstrapClassLoader加载,自定义的String无法完成加载。
在这里插入图片描述

4.源码解析

public abstract class ClassLoader {
	//每个ClassLoader都有自己的父级ClassLoader
    private final ClassLoader parent;
    //加载类方法
    public Class<?> loadClass(String name) throws ClassNotFoundException {
        return loadClass(name, false);
    }
    protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // 检查类是否已被加载
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                	//未被加载则查看父级加载器是否为null
                    if (parent != null) {
                    	//不为空则把加载操作给父级类加载
                        c = parent.loadClass(name, false);
                    } else {
                    	//为null说明此时已经是BootstrapClassLoader
                        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();
                    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;
        }
    }

5.Class.forName()和ClassLoader.loadClass()的区别

这两者都是加载类的,把.class文件转换为Class对象加载到jvm内存中,区别在于Class.forName会加载并初始化类,而Class Loader.loadClass()只是加载类,等到有new操作或者其他初始化类的时候才会真正的初始化类,这里也说明了Java中的类加载是懒加载机制,并不是在启动时初始化所有的类,启动时只是加载所有的类。

Class.forName()也是会调用ClassLoader.loadClass(),本质上也是用过类加载器来完成加载类的操作。

//Class里有ClassLoader变量,记录该类的类加载器
private final ClassLoader classLoader;
public static Class<?> forName(String className)
                throws ClassNotFoundException {
        Class<?> caller = Reflection.getCallerClass();
        //true就代表初始化类,参数列表中也有ClassLoader
        return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
    }

举例说明Class.forName()会初始化类:

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

public class ClassLoaderDemoTest {

    @Test
    public void testClassLoaderDemo(){
        try {
            Class clazz = Class.forName("com.jdk_source_code.demo.util.ClassLoaderDemo");
            System.out.println("########以下是采用类加载类加载类########");
            ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
            systemClassLoader.loadClass("com.jdk_source_code.demo.util.ClassLoaderDemo");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }


    }
}

结果:
init ClassLoaderDemo
########以下是采用类加载类加载类########

可以看出,Class.forName()会初始化类,静态代码块也会执行,而ClassLoader.loadClass()不会初始化类,Spring的IOC是用的ClassLoader,而数据库驱动是用的Class.forName(“com.mysql.cj.jdbc.Driver”),因为JDBC规范中明确要求Driver(数据库驱动)类必须向DriverManager注册自己,而注册的代码是在静态方法块中。

public class Driver extends NonRegisteringDriver implements java.sql.Driver {  
    // Register ourselves with the DriverManager  
    static {  
        try {  
            java.sql.DriverManager.registerDriver(new Driver());  
        } catch (SQLException E) {  
            throw new RuntimeException("Can't register driver!");  
        }  
    }  
    public Driver() throws SQLException {  
        // Required for Class.forName().newInstance()  
    }  
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值