类加载器

虚拟机判判定全限定名相同且使用同一个类加载器加载的类才是同一个类,否则会抛出ClassCastException。即使相同的字节码文件被 两个类加载器加载,那么这两个class对象也是不相等的,具体表现在Class对象的equal方法、isAssignableFrom、isInstance方法返回的接口, 也包含对象实例的instanceOf方法.

# 双亲委派模型

Bootstrap Loader
Bootstrap Loader
Extension Loader
Extension Loader
应用程序类加载器System Loader
应用程序类加载器System Loader
Custom Loader
Custom Loader
Custom Loader
Custom Loader
Viewer does not support full SVG 1.1
从虚拟机角度看只有两种不同的类加载器:1.启动类加载器(C++语言实现,虚拟机自身一部分);2.其他所有类加载器(Java语言实现,独立于虚拟机外,全部继承类java.lang.ClassLoader)

# 引导类加载器

负载加载JAVA_HOME/lib目录或者-Xbootclasspath参数指定路径下中存放的,并且虚拟机按照文件名识别,不符合名称的类库即使放在lib目录下也不会被加载。

# 扩展类加载器

sum.misc.Launcher$ExtClassLoader中以Java代码形式实现,负责加载JAVA_HOME/lib/ext目录下的类库,或者被java.ext.dirs系统变量指定路径中的类库。

# 应用类加载器

sum.misc.Launcher$AppClassLoader中以Java代码形式实现,负责加载用户类路径上的所有的类库,如果程序中没有自定过类加载器,那么一般情况下这个类加载器就是程序中默认 的类加载器。

  双亲委派模型,如果一个类加载器收到类加载的请求,它首先不会尝试自己去加载这个类,会把加载需求传递到上层类加载器,因此所有的类加载请求最终 都应该传递到顶层的启动类加载器,只有当父类加载器反馈自己无法加载,下层加载器才会尝试自己去完成加载。
  这里其实有个疑问,为什么一开始不直接从bootstrap类加载器,自上而下加载?万能的搜索大神这样回答,如果有多个自定义类加载器,都是平级,自上而下加载就不知道 选择哪个类加载器了。那疑问又来了,那意思是双亲委派模型回头自上而下就能知道使用哪个平级的类加载器咯?那既然双亲委派可以,直接自上而下加载为什么不可以?
  双亲委派模型带来了哪些好处?1.避免不同类加载器重复加载相同的类,导致程序中出现ClassCastException。2.避免用户自定义的类替换HOME包下的核心类。

# 破坏双亲委派模型

jdk1.2版本之后才引入双亲委派模型,1.2之前直接重写loadClass方法加载。个人认为不在破坏双亲委派原则之列,1.2之后为了兼容已经重写loadClass方法的代码, 在java.lang.ClassLoader类中新增加了findClass方法,引用用户编写的类加载逻辑重写findClass方法而不是直接在loadClass方法中编写代码。 loadClass方法,双亲委派的具体逻辑就实现在其中,如果父类加载失败,会自动调用自己的findClass方法来加载,既不会破坏原则,又能按照用户意愿加载类。
  双亲委派模型破坏在于模型自身的缺陷,越基础的类(jdk/lib)越是由上层的类加载器加载,如果基础类型需要调用用户代码,上层类加载器又不能加载用户类,这并非 不可能出现的情况,最典型的例子就是JNDI服务,它的代码由bootstrap加载器加载(1.3时移动到rt.jar),JNDI存在目的就是对资源进行查找和集中管理, 它需要调用其他厂商实现并部署在classpath下的JNDI服务提供者接口的代码,启动类加载器不识别这种代码,Java设计团队引入了上线文类加载器,这个类加载器通过 java.lang.Thread.setContextClassLoader()方法进行设置,如果创建线程时没有设置,那么它会从父线程继承一个,如果应用程序在全局都没有设置的话, 类加载就会默认为SystemClassLoader.Java中设计SPI加载基本都是使用这种方式来完成,eg:JNDI,JDBC... 父ClassLoader 可以使用当前线程 Thread.currentThread().getContextClassLoader() 所指定的 classloader 加载的类。