做网站,就上凡科建站百度手机助手网页
双亲委派模式
- 什么是类加载器?
- 双亲委派模式
- 自定义类的加载器
- 优势
- OSGI
- 类的卸载
什么是类加载器?
实现通过一个类的全限定名来获取描述此类二进制字节流的动作的的代码模板就叫做“类加载器”。
从JVM的角度,可以将类加载器划分为 以下两种:
- JVM 自带的加载器(是JVM组成的一部分,有C++语言编写)
- 用户自定的加载器(独立于JVM之外的加载器,使用Java语言编写)
从Java开发者的角度来划分,可以分为以下四种:
- 根加载器(Bootstrap): 加载C:\Program Files (x86)\Java\jre1.8.0_191\lib\rt.jar(,该jar包含了平时编写代码大部分jdk API); 或由-Xbootclasspath参数指定的jar
- 扩展类加载器(Extension),加载C:\Program Files (x86)\Java\jre1.8.0_191\lib\ext*.jar;或通过-Djava.ext.dirs参数指定的jar
- 系统加载器(AppClassLoader/SystemClassLoader):加载classpath。或由 -Djava.class.path =类/jar指定的类或jar
- 用户自定义的类加载器:都是抽象类Java.lang.ClassLoader的子类
双亲委派模式
双亲委派模式机制:
当一个加载器要加载类时,先价差是否已经被加载过,若没有则调用父加载器的loadClass() 方法,逐层向上交由双亲加载;若父加载器为空则默认使用启动类加载器作为父加载器; 当双亲中的某一个加载器加载成功后,再向下返回成功。如果父类加载均失败,则抛出ClassNotFoundException,再调用自己的findClass()方法进行加载。
package parents;class MyClass{}
public class TestClassLoader {public static void main(String[] args) throws ClassNotFoundException {Class clazz = Class.forName("parents.MyClass");ClassLoader classLoader = clazz.getClassLoader();//获得该类的来加载器//getClass()获得类加载器名System.out.println(classLoader.getClass());//通过ClassLoader提供的静态方法getSystemClassLoader获得 SystemClassLoaderClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();System.out.println( systemClassLoader.getClass() );System.out.println("-----");ClassLoader parent1 = systemClassLoader.getParent();System.out.println(parent1);System.out.println("-----");ClassLoader parent2 = parent1.getParent();System.out.println(parent2);System.out.println("-----");Class<?> clazz1 = Class.forName("java.lang.String");ClassLoader classLoader1 = clazz1.getClassLoader();System.out.println(classLoader1);}
}
执行结果:
class jdk.internal.loader.ClassLoaders$AppClassLoader
class jdk.internal.loader.ClassLoaders$AppClassLoader
-----
jdk.internal.loader.ClassLoaders$PlatformClassLoader@10f87f48
-----
null
-----
null
在JDK中的官方说明: Some implementations may use null to represent the bootstrap class loader。所以最后一个null表示bootstrap class loader.
AppClassLoader 和系统类加载器是同一个意思。所以 可以通过ClassLoader了的静态方法getSystemClassLoader()获得。
总结:
如果类是rt.jar中的,则该类由根加载器(Bootstrap Class Loader)加载。如果是自定义类则有系统加载器(App Class Loader)
自定义类的加载器
二进制名binary names,
Any class name provided as a String parameter to methods in ClassLoader must be a binary name as defined by The Java™ Language Specification.
Examples of valid class names include:
“java.lang.String”
“javax.swing.JSpinnerDefaultEditor""java.security.KeyStoreDefaultEditor" "java.security.KeyStoreDefaultEditor""java.security.KeyStoreBuilder$FileBuilder$1”
“java.net.URLClassLoader$3$1”
Any package name provided as a String parameter to methods in ClassLoader must be either the empty string (denoting an unnamed package) or a fully qualified name (全限定类名)as defined by The Java™ Language Specification.
$ 代表内部类;$数字代表第几个匿名内部类。
JDK 中文档注释
The class loader for an array class, as returned by {@link* Class#getClassLoader()} is the same as the class loader for its element* type; if the element type is a primitive type, then the array class has no* class loader.
- 数组的加载器类型和数组元素的加载器类型是相同的
- 原生类型(不是类)的数组是没有类加载器的。(显示结果为null)
class的不一定来自本地,可能是网络或jsp。若为 后者则需实现findClass(String name) 和byte[] loadClassData(String name) 方法。
* <p> The network class loader subclass must define the methods {@link* #findClass findClass} and {@code loadClassData} to load a class* from the network. Once it has downloaded the bytes that make up the class,* it should use the method {@link #defineClass defineClass} to* create a class instance. A sample implementation is:** <blockquote><pre>* class NetworkClassLoader extends ClassLoader {* String host;* int port;** public Class findClass(String name) {* byte[] b = loadClassData(name);* return defineClass(name, b, 0, b.length);* }** private byte[] loadClassData(String name) {* // load the class data from the connection* . . .* }* }* </pre></blockquote>
package parents;import java.io.*;//自定义类加载器
public class ClassLoaderImpl extends ClassLoader{//构造方法public ClassLoaderImpl(){super();}public Class findClass(String name) {byte[] b = loadClassData(name);return defineClass(name, b, 0, b.length);}/*** 加载指定类名对应字节码文件* @param name 例如:parents.Myclass* @return*/private byte[] loadClassData(String name) {// load the class data from the connection//将限定全类名中的 "." 替换成路径分隔符 "\"name = name.replace('.','\\') + ".class";FileInputStream in = null;//InputStream 变成 byte[]ByteArrayOutputStream out = null;byte [] result = null;try {in = new FileInputStream(new File(name));out = new ByteArrayOutputStream();int len = 0;byte[] buff = new byte[100];while( ( len = in.read(buff)) != - 1){out.write(buff,0, len);}result = out.toByteArray();} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}finally {try {if(in != null )in.close();if(out != null ) out.close();}catch (Exception e){e.printStackTrace();}}return result;}public static void main(String[] args) throws ClassNotFoundException {ClassLoaderImpl cl = new ClassLoaderImpl();Class<?> aClass = cl.loadClass("parents.MyClass1");System.out.println(aClass);System.out.println(aClass.getClassLoader());}
}//自定义类
class MyClass1{}
注释掉MyDfClass,并且删除out路径下原有编译号的字节码文件MyDfClass.class,重新编译ClassLoaderImpl
package parents;import java.io.*;//自定义类加载器
public class ClassLoaderImpl extends ClassLoader{static String path;//构造方法public ClassLoaderImpl(){super();}public Class findClass(String name) {byte[] b = loadClassData(name);return defineClass(name, b, 0, b.length);}/*** 加载指定类名对应字节码文件* @param name 例如:parents.+Myclass1* @return*/private byte[] loadClassData(String name) {// load the class data from the connection//构造本地路径name = path + name.substring( name.lastIndexOf('.') + 1 ) + ".class";;System.out.println(name);FileInputStream in = null;//InputStream 变成 byte[]ByteArrayOutputStream out = null;byte [] result = null;try {in = new FileInputStream(new File(name));out = new ByteArrayOutputStream();int len = 0;byte[] buff = new byte[100];while( ( len = in.read(buff)) != - 1){out.write(buff,0, len);}result = out.toByteArray();} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}finally {try {if(in != null )in.close();if(out != null ) out.close();}catch (Exception e){e.printStackTrace();}}return result;}public static void main(String[] args) throws ClassNotFoundException {ClassLoaderImpl cl = new ClassLoaderImpl();cl.path = "D:\\study\\";Class<?> aClass = cl.loadClass("parents.MyDfClass");System.out.println(aClass);System.out.println(aClass.getClassLoader());}
}//自定义类
//class MyDfClass{
//
//}
执行结果:
D:\study\MyDfClass.class
class parents.MyDfClass
parents.ClassLoaderImpl@723279cf
通过阅读源码,可以知道在双亲委托机制中,“下面”的加载器通过parent引用“上面”的加载器。即在双亲委派机制中,各个加载器之间不是继承关系。
public abstract class ClassLoader {private static native void registerNatives();static {registerNatives();}// The parent class loader for delegation// Note: VM hardcoded the offset of this field, thus all new fields// must be added *after* it.private final ClassLoader parent;
ClassLoader中的loadclass源码解读
protected Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException{synchronized (getClassLoadingLock(name)) {// First, check if the class has already been loaded//1、首先使用findLoadedClass(name)检查是否该类被加载过Class<?> c = findLoadedClass(name);if (c == null) {long t0 = System.nanoTime();try {//如果“父类”引用不为空,则委托“父类”加载if (parent != null) {c = parent.loadClass(name, false);} else {//如果“父类”为空,说明双亲委派的顶层了,就调用顶层的加载器(Bootstrap)c = findBootstrapClassOrNull(name);}} catch (ClassNotFoundException e) {// ClassNotFoundException thrown if class not found// from the non-null parent class loader}//如果父类都查找失败了 则调用自定义的findclass()方法去加载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 statsPerfCounter.getParentDelegationTime().addTime(t1 - t0);PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);PerfCounter.getFindClasses().increment();}}if (resolve) {resolveClass(c);}return c;}}
有抽象方法的类一定是抽象类。但是抽象类中不一定都是抽象方法,也可以全是具体方法。抽象类也可以拥有具体实现了的方法,只是不能直接实例化(不能直接new 抽象方法);比如java.lang.ClassLoader虽为抽象类
优势
双亲委派机制的优势:
可以防止用户自定义的类和rt.jar中的类重名而造成的混乱。
如果两个类存在继承关系,继承的双方(父类、子类)必须都是同一个 类加载器,否则会 由于命名空间问题而出错。
如果两个类不存在继承关系,子类加载器可以访问父类加载器加载的类(自定义类加载器可以访问到父类加载器加载的类);反之则不行,即父类加载器不能访问子类加载器。
OSGI
- 网状结构的委派模式
- 屏蔽掉了硬件的异构性。例如,可以将项目部署在网络上,可以在A节点上远程操作B节点。在操作上,可以对硬件无感,也可以在A节点上对B节点上的项目进行运维、部署,并且在维护期间理想情况下无需重启和暂停。
类的卸载
- 系统自带的类加载器加载的类是不会 被卸载的
- 用户自定义的加载器,会被GC卸载