1、类加载简介java
类加载机制:JVM把class文件加载到内存,并对数据进行校验、解析和初始化,最终造成JVM能够直接使用的Java类型的过程。apache
2、bootstrap
加载:将class文件字节码内容经过类加载器加载到内存中,并将这些静态数据转换成方法区中的运行时数据结构,在堆中生成一个表明这个类的java.lang.Class对象,做为方法区类数据的访问入口(外部能够经过Class对象,做为操做类的入口)。这个过程须要类加载器参与。数组
连接:将Java类的二进制代码合并到JVM的运行状态之中的过程。【验证:确保加载的类信息符合JVM规范,没有安全方面的问题。准备:正式为类变量(static变量)分配内存并设置类变量初始值的阶段,这些内存都将在方法区中进行分配。解析:虚拟机常量池内的符号引用替换为直接引用的过程。】缓存
初始化:1.初始化阶段是执行类构造器<clinit>()方法的过程。类构造器<clinit>()方法是由编译器自动收集类中的全部类变量的赋值动做和静态语句块(static块)中的语句合并产生的。 2.当初始化一个类的时候,若是发现其父类尚未进行过初始化,则须要先触发其父类的初始化。 3.虚拟机会保证一个类的<clinit>()方法在多线程环境中被正确加锁和同步。当访问一个Java类的静态域时,只有真正声明这个域的类才会被初始化。tomcat
类的主动引用(必定会发生类的初始化)安全
- new 一个类的对象服务器
- 调用类的静态成员(除了final常量)和静态方法网络
- 使用java.lang.reflect包的方法对类进行反射调用数据结构
- 当虚拟机启动,java Hello,则必定会初始化Hello类,说白了就是先启动main方法所在的类
- 当初始化一个类,若是其父类没有被初始化,则先会初始化他的父类
类的被动引用(不会发生类的初始化)
- 当访问一个静态域时,只有真正声明这个域的类才会被初始化
. 经过子类引用父类的静态变量,不会致使子类初始化
- 经过数组定义类引用,不会触发此类的初始化
- 引用常量不会触发此类的初始化(常量在编译阶段就存入调用类的常量池中了)
3、
public class Demo01 { public static void main(String[] args) { System.out.println("初始化Demo01"); //A a = new A(); // System.out.println("width= " + A.width); // System.out.println(A.MAX); // System.out.println(Child_A.width); A[] arr = new A[]{}; } } class A extends Father_A { static { System.out.println("静态初始化块"); width = 200; } public static int width = 100; public static final int MAX = 100; public A() { System.out.println("构造函数初始化中"); } { System.out.println("初始化块"); width = 300; } } class Father_A { static { System.out.println("静态初始化块——Father_A"); } } class Child_A extends A { static { System.out.println("静态初始化Child_A"); } }
4、深刻类加载器
类加载器的做用:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区中的运行时数据结构,在堆中生成一个表明这个类的java.lang.Class对象,做为方法区类数据的访问入口。
类缓存:标准的JavaSE类加载器能够按要求查找类,但一旦某个类被加载到类加载器中,它将维持加载(缓存)一段时间。不过,JVM垃圾收集器能够回收这些Class对象。
类加载器的层次结构(树状结构)
引导类加载器(bootstrap class loader)
- 它用来加载Java的核心库(JAVA_HOME/jre/lib/rt.jar,或sum.boot.class.path路径下的内容),是用原生代码来实现的,并不继承自java.lang.ClassLoader。
- 加载扩展类和应用程序类加载器,并指定他们的父类加载器。
扩展类加载器(extensions class loader)
- 用来加载Java的扩展库(JAVA_HOME/jre/ext/*.jar,或java.ext.dirs路径下的内容)。Java虚拟机的实现会提供一个扩展库目录。该类加载器在此目录路面查找并加载Java类。
- 由sum.misc.Launcher$ExtClassLoader实现
应用程序类加载器(application class loader)
- 它根据Java应用的类路径(classpath,java.class.path路径)。通常来讲,Java应用的类都是由它来完成加载的。
- 由sun.misc.Launcher$AppClassLoader实现。
自定义类加载器
自定义类加载器的流程
- 继承:java.lang.ClassLoader
- 首先检查请求的类型是否已经被这个类装载器装载到命名空间中了,若是已经装载,直接返回;
- 委派类加载请求给父类加载器,若是父类加载器可以完成,则返回父类加载器加载的Class实例;
- 调用本类加载器的findClass(...)方法,试图获取对应的字节码,若是获取的到,则调用defineClass(...)导入类型到方法区;若是获取不到对应的字节码或者其它缘由失败,返回异常给loadClass(...),loadClass(...)转抛异常,终止加载过程;
- 注意:被两个类加载器加载的同一个类,JVM不认为是相同的类。
文件类加载器
import org.apache.commons.io.IOUtils; import java.io.ByteArrayOutputStream; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; public class FileSystemClassLoader extends ClassLoader { private String rootDir; public FileSystemClassLoader(String rootDir) { this.rootDir = rootDir; } @Override protected Class<?> findClass(String name) throws ClassNotFoundException { Class<?> clazz = findLoadedClass(name); /** * 先查询有没有加载过这个类,若是已经加载,则直接返回加载好的类;若是没有,则加载新的类 */ if (clazz != null) { return clazz; } else { /** * 委派类加载请求给父类加载器 */ ClassLoader parent = this.getParent(); try { clazz = parent.loadClass(name); } catch (Exception e) { } if (clazz != null) { return clazz; } else { byte[] classData = getClassData(name); if (classData == null) { throw new ClassNotFoundException(); } else { clazz = defineClass(name, classData,0, classData.length); } } } return clazz; } private byte[] getClassData(String classname) { String path = rootDir + "/" + classname.replace(".", "/") + ".class"; /** * 注:IOUtils,可使用它将流中的数组转换成字节数组 */ InputStream is = null; ByteArrayOutputStream baos = new ByteArrayOutputStream(); try { is = new FileInputStream(path); byte[] buffer = new byte[1024]; int temp = 0; while ((temp = is.read(buffer)) != -1) { baos.write(buffer, 0, temp); } return baos.toByteArray(); } catch (Exception e) { e.printStackTrace(); return null; } finally { try { if (is != null) { is.close(); } if (baos != null) { baos.close(); } }catch (IOException e) { e.printStackTrace(); } } /* InputStream is = null; try { is = new FileInputStream(path); byte[] bytes = IOUtils.toByteArray(is); return bytes; } catch (Exception e) { e.printStackTrace(); return null; } finally { try { if (is != null) { is.close(); } } catch (Exception e) { e.printStackTrace(); } }*/ } }
ipublic class Demo02 { public static void main(String[] args) throws ClassNotFoundException { FileSystemClassLoader classLoader = new FileSystemClassLoader("/Users/Work/mimidai/mimidai-test"); FileSystemClassLoader classLoader2 = new FileSystemClassLoader("/Users/Work/mimidai/mimidai-test"); Class<?> clazz = classLoader.loadClass("HelloWordI"); Class<?> clazz1 = classLoader.loadClass("HelloWordI"); Class<?> clazz2 = classLoader2.loadClass("HelloWordI"); Class<?> clazz3 = classLoader2.loadClass("java.lang.String"); Class<?> clazz4 = classLoader2.loadClass("com.mimidai.test2.Demo01"); System.out.println(clazz.hashCode()); System.out.println(clazz1.hashCode()); System.out.println(clazz2.hashCode()); System.out.println(clazz.getClassLoader()); System.out.println(clazz2.getClassLoader()); System.out.println(clazz3.getClassLoader()); System.out.println(clazz4.getClassLoader()); } }
输出结果
363771819
363771819
2065951873
com.mimidai.test2.FileSystemClassLoader@16b98e56
com.mimidai.test2.FileSystemClassLoader@27d6c5e0
null
sun.misc.Launcher$AppClassLoader@18b4aac2
网络类加载器
import org.apache.commons.io.IOUtils; import java.io.InputStream; import java.net.URL; public class NetClassLoader extends ClassLoader { private String rootURL; public NetClassLoader(String rootURL) { this.rootURL = rootURL; } @Override protected Class<?> findClass(String name) throws ClassNotFoundException { Class<?> clazz = this.findLoadedClass(name); if (clazz != null) { return clazz; } else { ClassLoader parentClassLoader = this.getParent(); try { clazz = parentClassLoader.loadClass(name); } catch (Exception e) { } if (clazz != null) { return clazz; } else { byte[] classData = getClassData(name); if (classData != null) { clazz = defineClass(name, classData,0, classData.length); return clazz; } else { throw new ClassNotFoundException(); } } } } private byte[] getClassData(String name) { String path = rootURL + name.replace(".", "/"); InputStream is = null; try { URL url = new URL(path); is = url.openStream(); byte[] clazzDate = IOUtils.toByteArray(is); return clazzDate; } catch (Exception e) { try { if (is != null) { is.close(); } } catch (Exception e1) { } return null; } } }
加密解密类加载器(取反操做,DES对称加密解密)
5、java.lang.ClassLoader类介绍
- java.lang.ClassLoader类的基本职责就是根据一个指定的类的名称,找到或者生成器对应的字节代码,而后从这些字节代码中定义出一个Java类,即java.lang.Class类的一个实例。
- 除此以外,ClassLoader还负责加载Java应用所需的资源,如图像文件和配置文件等。
- 相关方法
- getParent() 返回该类加载器的父类加载器。
- loadClass(String name) 加载名称为name的类,返回的结果是java.lang.Class类的实例。
. 此方法负责加载指定名字的类,首先会从已加载的类中去寻找,若是没有找到;从parent ClassLoader[ExtClassLoader]中加载;若是没有加载到,则从Bootstrap ClassLoader中尝试加载(findBootstrapClassOrNull方法),若是仍是加载失败,则本身加载。若是还不能加载,则抛出异常ClassNotFoundException。
. 若是要改变类的加载顺序能够覆盖此方法;
- findClass(String name) 查找名称为name的类,返回的结果是java.lang.Class类的实例。
- findLoadedClass(String name) 查找名称为name的已经被加载过的类,返回的结果是java.lang.Class类的实例。
- defineClass(byte[] b, int off, int len) 把字节数组b中的内容转换成Java类,返回的结果是java.lang.Class类的实例。
- resolveClass(Class<?> c) 连接指定的Java类
6、类加载器的代理模式
代理模式
- 交给其余加载器来加载指定的类
双亲委托机制
- 就是某个特定的类加载器在接到加载类的请求时,首先将加载任务委托给父类加载器,依此追溯,直到最高的爷爷辈的,若是父类加载器能够完成类加载任务,就成功返回;只有父类加载器没法完成此加载任务时,才本身去加载。
- 双亲委托机制是为了保证Java核心库的类型安全。
. 这种机制就保证不会出现用户本身能定义java.lang.Object类的状况。
- 类加载器除了用于加载类,也是安全的最基本的屏障。
- 双亲委托机制只是代理模式的一种
. 并非全部的类加载器都采用双亲委托机制。
. tomcat服务器类加载器也使用代理模式,所不一样的是它是首先尝试去加载某个类,若是找不到在代理给父类加载器。这与通常类加载器的顺序是相反的。