ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
[TOC] # 类的加载器 ![](https://img.kancloud.cn/d6/80/d6802bf1f9bf054a13cdd9dd0eb6957f_695x309.png) ![](https://box.kancloud.cn/739586cc003ac9577d30fb19953db921_766x268.png) ![](https://box.kancloud.cn/d5e8943aa8a830d490c4082f37335eab_823x294.png) 当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,链接,初始化三步来实现对这个类进行初始化。 * **加载** 就是指将class文件读入内存,并为之创建一个Class对象。 任何类被使用时系统都会建立一个Class对象. 通过类的全限定名获取二进制字节流,将二进制字节流转换成方法区中的运行时数据结构,在内存中生成Java.lang.class对象; <br> * **链接** 执行下面的校验、准备和解析步骤,其中解析步骤是可以选择的; 校验:检查导入类或接口的二进制数据的正确性;(文件格式验证,元数据验证,字节码验证,符号引用验证) 准备:给类的静态变量分配并初始化存储空间; 解析:将常量池中的符号引用转成直接引用; <br> * **初始化** 激活类的静态变量的初始化Java代码和静态Java代码块,并初始化程序员设置的变量值 # 类的初始化时机 1. 创建类的实例 2. 类的静态变量,或者为静态变量赋值 3. 类的静态方法 4. 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象 5. 初始化某个类的子类 6. 直接使用java.exe命令来运行某个主类 # 类加载器的组成 * Bootstrap ClassLoader 根类加载器 也被称为引导类加载器,负责Java核心类的加载 比如System,String等。在JDK中JRE的lib目录下rt.jar文件中 * Extension ClassLoader 扩展类加载器 负责JRE的扩展目录中jar包的加载。 在JDK中JRE的lib目录下ext目录 * System ClassLoader 系统类加载器 负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径 # 类加载器的种类 类加载器有三种,不同类加载器加载不同的 ![](https://box.kancloud.cn/a155ccf73f778db3d3536d30841fd8bf_623x267.png) 1. BootStrap:引导类加载器:加载都是最基础的文件 2. ExtClassLoader:扩展类加载器:加载都是基础的文件 3. AppClassLoader:应用类加载器:三方jar包和自己编写java文件 怎么获得类加载器? ~~~ ClassLoader 字节码对象.getClassLoader(); ~~~ ~~~ //获取系统类加载器 ClassLoader classLoader1 = ClassLoader.getSystemClassLoader(); System.out.println(classLoader1); //获取扩展类加载器 ClassLoader classLoader2 = classLoader1.getParent(); System.out.println(classLoader2); //获取引导类加载器(获取不到) ClassLoader classLoader3 = classLoader2.getParent(); System.out.println(classLoader3); //说明:用户自定义类是由系统类加载器加载的 ClassLoader loader1 = Person.class.getClassLoader(); System.out.println(loader1); //说明:java 核心api是由引导类加载器加载的 ClassLoader loader2 = Class.forName("java.lang.String").getClassLoader(); System.out.println(loader2); ~~~ # 分析类的加载 Java在需要使用类的时候,才会将类加载,Java的类加载是由类加载器来完成的。 当在命令行模式下执行java XXX.class指令后,java运行程序会尝试找到JRE安装的所在目录,然后寻找jvm.dll(默认是在JRE目录下bin\client目录中),接着启动JVM并进行初始化动作,产生Bootstrap Loader,Bootstrap Loader会加载Extended Loader,并设定Extende Loader的parent为Bootstrap Loader。 接着Bootstrap Loader会加载System Loader,并将System Loader的parent设定为Extended Loader。 Bootstrap Loader通常由C编写而成,Extended Loader是由Java编写而成,实际是对应于sun.misc.Launcher$ExtClassLoader(Launcher中的内部类).System Loader是由java编写而成,实际对你关于 sun.misc.Launcher$AppClassLoader(Launcher中的内部类)。 Bootstrap Loader会搜索系统参数sun.boot.class.path中指定位置的类,默认是JRE所在目录的classes下的.class文件,或lib目录下.jar文件中(如tr.jar)的类并加载。 可用System.getProperty(“sun.boot.class.path”)来显示sun.boot.class.path中指定的路径。 Extended Loader是由Java编写而成,会搜索系统参数java.ext.dirs中指定位置的类,默认是JRE目录下的lib\ext\classes目录下的.class文件,或lib\ext目录下的.jar文件中的类并加载。 System Loader是由java编写而成,会搜索系统参数java.class.path中指定位置的类,也就是Classpath所指定的路径,默认是当前工作路径下的.class文件。 在加载类时,每个类加载器会先将加载类的任务交给其parent,如果parent找不到,再由自己负责加载。所以在加载类顺序为:`Bootstrap Loader——》Extended Loader——》System Loader`的顺序来寻找类,若都找不到,会抛出NoClassDefFoundError. 类加载器在Java中是以java.lang.ClassLoader类型存在,每一个类被加载后,都会有一个Class的实例来代表,而每个Class的实例都会记得自己是由哪个ClassLoader加载的。 可以由Class的getClassLoader()取得加载该类的ClassLoader,而从ClassLoader的getParent()方法可以取得自己的parent # 分析 Class.forName()和ClassLoader.loadClass ## Class.forName Class.forName(className)方法,内部实际调用的方法是Class.forName(className,true,classloader); 第2个boolean参数表示类是否需要初始化,Class.forName(className)默认是需要初始化。 一旦初始化,就会触发目标对象的 static块代码执行,static参数也也会被再次初始化。 ## ClassLoader.loadClass ClassLoader.loadClass(className)方法,内部实际调用的方法ClassLoader.loadClass(className,false); 第2个boolean参数,表示目标对象是否进行链接,false表示不进行链接,由上面介绍可以, 不进行链接意味着不进行包括初始化等一些列步骤,那么静态块和静态对象就不会得到执行 # 数据库链接为什么使用Class.forName(className) JDBC Driver源码如下,因此使用Class.forName(classname)才能在反射回去类的时候执行static块 ~~~ static { try { java.sql.DriverManager.registerDriver(new Driver()); } catch (SQLException E) { throw new RuntimeException("Can't register driver!"); } } ~~~ # 加载指定目录下的配置文件 ~~~ //获得Demo字节码文件的类加载器 Class clazz = Demo.class;//获得Demo的字节码对象 ClassLoader classLoader = clazz.getClassLoader();//获得类加载器 //getResource的参数路径相对classes(src) //获得classes(src)下的任何的资源 String path = classLoader.getResource("com/wdsa/classloader/jdbc.properties").getPath(); //classLoader.getResourceAsStream(""); System.out.println(path); ~~~