首先介绍下ClassLoader:
ClassLoader顾名思义就是类加载器,负责将Class加载到JVM中,事实上ClassLoader除了能将Class加载到JVM中以外,还有一个重要的做用就是审查每一个类应该有谁加载,ClassLoader是一个父优先的等级加载机制。ClassLoader除了上述两个做用外还有一个任务就是将Class字节码从新解析成JVM统一要求的对象格式-----------由此本文能够划分红三点java
ClassLoader类结构分析
1. ClassLoader经常使用的方法
1,defineClass():用来将byte字节流解析成JVM可以识别的Class对象
2,findClass():用来子类扩展的方法,目的是为了寻找类
3,loadClass():加载类,这里就能够动态加载了,程序运行的时候加载
4,resolveClass():这个方法是用来连接这个类
JDK给咱们提供了能够扩展的,也就是咱们能够自定义的类加载器URLCLassLoader,比较方便app
2. ClassLoader的等级加载机制
1,BootStrap ClassLoader:
这是一个引导类加载器,首先声明一下虽然说在类的结构中能够看到这个类,可是这个类只有Class文件,据了解这个类是经过C++编写的,这也就是ExtClassLoader的父类为空的缘由。引导类负责加载JDK的jre下的类库,例如rt.jarjsp
2,ExtClassLoader:
这是一个扩展类加载器,加载bin目录下的ext文件夹下的jar包,很少说ide
3,AppClassLoader:
这个类加载器就是咱们自定义的类,例如咱们的这个方法getSystemClassLoader()做为父加载器,这个加载器也就是AppClassLoader。测试
4,加载类过程总结:
看了不少资料都是什么双亲委托加载机制,具体我也不是很清楚,能够我太low,可是我我的的理解就是,当加载一个类的时候:首先检查这个类是否已经被本身加载过,若是已经加载过,就拒绝本次加载,若是没有加载过,就会抛给他的父类,而后父类在检查是否加载,而后在抛给父类,直到所有检查完都没有发现这个类被加载,那么就有意思了,由于每个类加载器都有本身的加载范围,而后开始判断,父类不加载类,那么就开始向下寻找这个类的加载器,而后这个类被加载,也就是先向上检查是否加载,其次向下看是否能够加载!--------------------这里有一个讲解,来帮助理解,若是咱们本身写了一个java.lang.String,那么类加载器就先去加载jdk下的java.lang.String.遇到咱们本身定义的类就不会加载了,这也就是java.lang.Object首先被加载,父类加载会防止咱们破坏干扰jre的正常运行,致使类结构被破坏this
这里是类关系的代码spa
public class ClassA { } public class ClassB { } public class ClassTest { public static void main(String[] args) { //演示 我們的類加載器 System.out.println("ClassTest-------------ClassLoader:"+ClassTest.class.getClassLoader()); System.out.println("ClassTest.Parent------ClassLoader:"+ClassTest.class.getClassLoader().getParent()); System.out.println("ClassTest.GrandFather-ClassLoader:"+ClassTest.class.getClassLoader().getParent().getParent()); //用户自定义的类加载 都是经过AppClassLoader加载 System.out.println(ClassA.class.getClassLoader()); System.out.println(ClassB.class.getClassLoader()); //结论就是----------当两个类名称不一样,那么这两个类就不相同 } }
打印结果3d
ClassTest-------------ClassLoader:sun.misc.Launcher$AppClassLoader@73d16e93 ClassTest.Parent------ClassLoader:sun.misc.Launcher$ExtClassLoader@15db9742 ClassTest.GrandFather-ClassLoader:null sun.misc.Launcher$AppClassLoader@73d16e93 sun.misc.Launcher$AppClassLoader@73d16e93
这里就不须要介绍了,只要类名不一样,那么类确定也不一样
package me.classloader.classpackage; public class String { } package me.classloader.classpackage; import org.junit.Test; public class ClassTest { @Test public void classTest(){ //这里是 BootStrap ClassLoader-------所以返回Null System.out.println(java.lang.String.class+"------classLoader:"+java.lang.String.class.getClassLoader()); //这里 咱们自定义的类 被AppClassLoader 加载 System.out.println(String.class+"--------ClassLoader:"+String.class.getClassLoader()); //很明显这两个类是不一样的 } }
这里能够获得咱们的包名能够引发 两个类不是同一个类,而且,这里JVM就近调用一个类先调用的同包下的类
package me.classloader.classloader; public class Demo { private Demo demo; public void setDemo(Demo demo) { this.demo = demo; } } package me.classloader.classloader; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; //本身写的ClassLoader public class MyClassLoader extends ClassLoader { private String classPath;// 类路径 public MyClassLoader() { } public MyClassLoader(String classPath) { this.classPath = classPath; } @Override protected Class<?> findClass(String name) throws ClassNotFoundException { byte[] classDate = getDate(name); if (classDate == null) { throw new ClassNotFoundException(); } else { return defineClass(name, classDate, 0, classDate.length); } } // 将 class 文件转化成字节流 //className是类名称---权限定名 private byte[] getDate(String className) { // 将XX.XX.java --------->XX/XX/java.class String path = classPath + File.separatorChar + className.replace('.', File.separatorChar) + ".class"; try { InputStream is = new FileInputStream(path); ByteArrayOutputStream stream = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int num = 0; while ((num = is.read(buffer)) != -1) { stream.write(buffer, 0, num); } return stream.toByteArray(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } return null; } } package me.classloader.classloader; public class ClassTest { public static void main(String[] args) throws Exception{ String classPath ="E:\\javaprogram\\java-exercise\\classload\\bin"; String className = "me.classloader.classloader.Demo"; ClassLoader appClassLoader = ClassTest.class.getClassLoader(); MyClassLoader myClassLoader = new MyClassLoader(classPath); Class demo1 = appClassLoader.loadClass(className); System.out.println(demo1.newInstance().getClass().getClassLoader()); Class demo2 = myClassLoader.findClass(className); System.out.println(demo2.newInstance().getClass().getClassLoader()); System.out.println(demo1.newInstance().getClass() ==demo2.newInstance().getClass()); } }
测试结果 sun.misc.Launcher$AppClassLoader@73d16e93 me.classloader.classloader.MyClassLoader@6d06d69c false
经过上面的代码,咱们也知道了如何写一个类加载器,其实查看findClass()是ClassLoader提供的一个模板方法!代理
总结:类加载器也是一个比较深的东西,例如servlet中jsp使用了热部署!其实就是类加载器的应用,可能动态代理也用到了类加载器,由于实际工做加载的是咱们代理的类code