1.类与类加载器
2.类加载机制的基本特征
3.类加载器的分类
1.启动类加载器:C++实现,是虚拟机自身的一部分
2.其他所有类加载器:由Java实现,独立于虚拟机外部,并且全部继承自抽象类java.lang.ClassLoader。
4.类加载器的介绍
4.1 启动类加载器
存放路径:<JAVA_HOME>\lib,或者是-Xbootclasspath指定的路径。(而且是虚拟机能够识别的)
实现:C/C++实现
作用:提供JVM自身所需要的类
使用方法:启动类加载器无法被Java程序直接引用。如果需要把加载请求委派给类加载器去处理,直接使用null代替即可。
4.2 扩展类加载器
存放路径:<JAVA_HOME>\lib\ext目录,或者被java.ext.dirs系统变量所指定的路径中所有的类库。
实现:Java
作用:是一种Java系统类库的扩展机制,JDK的开发团队允许用户将具有通用性的类库放置在ext目录以扩展Java SE的功能。
使用方法:在程序中直接使用。
4.3 应用程序类加载器(系统类加载器)
实现:由sun.misc.Launcher$AppClassLoader实现。
作用:负责加载用户类路径(ClassPath)上所有的类库。
是ClassLoader类中的getSystemClassLoader()方法的返回值。
使用方法:直接使用。
4.4 自定义类加载器
5.双亲委派模型
5.1 类加载器双亲委派模型
5.2 双亲委派模型的工作过程
如果一个类收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求都应该传送到最顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请求(它的搜索范围中没有找到所需的类)时,子加载器才会尝试自己去完成加载。
5.3 实现双亲委派模型的代码
5.4 双亲委派模型的优点
1.避免类的重复加载,确保一个类的全局唯一性
Java类随着它的类加载器一起具备了一种带有优先级的层次关系,通过这种层次关系可以避免类的重复加载,当父亲已经加载了此类时,就没有必要ClassLoader再加载一次。
2.保护程序安全,防止核心API被随意篡改
5.5 双亲委派模型的缺点
检查类是否加载的委托过程是单向的,这个方式虽然从结构上说比较清晰,使各个ClassLoader的职责非常明确,但是同时会带来一个问题,即顶层的ClassLoader无法访问底层的ClassLoader所加载的类。
通常情况下,启动类加载器中的类为系统核心类,包括一些重要的系统接口,而在应用类加载器中为应用类。按照这种模式,应用类访问系统类自然是没有问题,但是系统类访问应用类就会出现问题。比如在系统类中提供了一个接口,该接口需要在应用类中得以实现,该接口还绑定一个工厂方法,用于创建该接口的实例,而接口和工厂方法都在启动类加载器中。这时,就会出现该工厂方法无法创建由应用类加载器的应用实例的问题。
6.三次双亲委派机制的破坏
6.1 第一次
第一次破坏发生在双亲委派模型出现之前(即jdk1.2)
6.2 第二次
由模型自身的缺陷导致。
线程上下文类加载器:这个类加载器可以通过java.lang.Thread类的setContextClassLoader()方法进行设置,如果创建线程时还未设置,它将会从父线程继承一个,如果在应用程序的全局范围内都没有设置过的话,那这个类加载器默认就是应用程序类加载器。
有了线程上下文类加载器,就可以父类加载器去请求子类加载器完成类加载。
Java 提供了很多服务提供者接口(Service Provider Interface,SPI),允许第三方为这些接口提供实现。常见的
SPI 有 JDBC、JCE、JNDI、JAXP 和 JBI 等。 这些 SPI 的接口由 Java 核心库来提供,而这些 SPI
的实现代码则是作为 Java 应用所依赖的 jar
包被包含进类路径(CLASSPATH)里。SPI接口中的代码经常需要加载具体的实现类。那么问题来了,SPI的接口是Java核心库的一部分,是由**启动类加载器(Bootstrap
Classloader)来加载的;SPI的实现类是由系统类加载器(System ClassLoader)**来加载的。引导类加载器是无法找到
SPI 的实现类的,因为依照双亲委派模型,BootstrapClassloader无法委派AppClassLoader来加载类。
而线程上下文类加载器破坏了“双亲委派模型”,可以在执行线程中抛弃双亲委派加载链模式,使程序可以逆向使用类加载器。
6.3 第三次
由用户对程序动态性的追求导致。
代码热替换
OSGI
7.沙箱安全机制
8.ClassLoader
9.JDK9的新特性