Java 虚拟机对 class 文件采用的是按需加载的方式,也就是说当需要使用该类时才会将它的 class 文件加载到内存生成 class 对象。而且加载某个类的 class 文件时,Java 虚拟机采用的是双亲委派模式,即把请求交由父类处理,它是一种任务委派模式。
1)如果一个类加载器收到了类加载请求,它并不会自己先去加载,而是把这个请求委托给父类的加载器去执行;
2)如果父类加载器还存在其父类加载器,则进一步向上委托,依次递归,请求最终将到达顶层的启动类加载器;
3)如果父类加载器可以完成类加载任务,就成功返回(就不会由子类加载器去加载了),倘若父类加载器无法完成此加载任务,子加载器才会尝试自己去加载,这就是双亲委派模式。
举例:
1.
出于安全考虑,Bootstrap 启动类加载器只加载包名为 java、javax、sun 等开头的类。一看你是java开头的,引导类加载就说了。这是归我管我来加载String(核心API里的String)。因此有父类来加载后,就不会再向下委托了,所以我们new 的这个String对象就是核心API里面的String类对象,而不是我们自定义的String,因此就没有打印出自定义String里的static静态资源里的语句
2.
委托到引导类加载器,它发现你这个包是jvm开头的,不归引导类加载管,就向下委托,也不归扩展类加载器管,所以最后回到系统类加载器来加载,因此最后输出结果就是系统类加载来进行的加载
3.
一直往上委托,就交给到了引导类加载器,它加载了String类以后,然后就想去执行main方法,但是核心API的String里面是没有main方法的,所以就报了 错误: 在类 java.lang.String 中找不到 main 方法. 可知,根本就没有试着想去加载我们自定义的String类,完全忽略掉你了
4.
当我们加载 jdbc.jar 用于实现数据库连接的时候,首先我们需要知道的是 jdbc.jar 是基于 SPI 接口进行实现的,所以在加载的时候,会进行双亲委派,最终从根加载器中加载 SPI 核心类,然后在加载 SPI 接口类,接着在进行反向委派,通过线程上下文类加载器进行实现类 jdbc.jar 的加载。
避免类的重复加载
保护程序安全,防止核心 API 被随意篡改
自定义类:java.lang.String
自定义类:java.lang.ShkStart(报错:阻止创建 java.lang 开头的类)
栗子:
分析:引导类加载器看到是 java.lang开头的,就表示这是归它管,于是就要去加载这个ShkStart类了,但直接直接给它报错了,相当于,要加载java.lang这个包,要想访问是要有权限的,现在报错就是阻止我们去直接用这个java.lang包来自定义这个ShkStart类。其实这也是起到了保护作用和出于安全的考虑,如果允许去加载这个类,加载成功的话,就会导致对引导类加载器本身造成影响,所以这里是直接把引导类加载器给整挂了。所以我们也禁止去用java.lang这样的包名去命名
其实这也是起到了保护作用和出于安全的考虑,如果允许去加载这个种自定义的类,加载成功的话,但里面可能会有一些恶意代码,就可能会会对现有的项目和程序进行破坏
自定义 String 类,但是在加载自定义 String 类的时候会率先使用引导类加载器加载,而引导类加载器在加载的过程中会先加载 jdk 自带的文件(rt.jar 包中 java\lang\String.class),报错信息说没有 main 方法,就是因为加载的是 rt.jar 包中的 string 类。这样可以保证对 java 核心源代码的保护,这就是沙箱安全机制。