参考:http://blog.csdn.net/briblue/article/details/54973413,http://blog.csdn.net/qbg19881206/article/details/8890600,http://zyjustin9.iteye.com/blog/2022654,https://www.cnblogs.com/tuning/p/6943427.html
一、ClassLoader的作用?
ClassLoader翻译过来就是类加载器。jvm启动的时候,并不会一次性加载所有的class文件,而是根据需要去动态地加载。ClassLoader的作用就是将class文件加载到jvm虚拟机中去。
二、java自带的三个类加载器
1。Bootstrap ClassLoader。最顶层的加载类,主要加载核心类库,加载%JRE_HOME%\\lib下的rt.jar、resources.jar、charsets.jar和class等。
2。Extention ClassLoader。扩展的类加载器,负责加载目录%JRE_HOME%\\lib\\ext目录下的jar包和class文件。
3。Appclass Loader,也称为SystemAppClass,加载当前应用的classpath的所有类。
4。我们可以自己写类加载器,加载类和资源。
三、类加载器间的关系
AppClassLoader的父加载器是ExtClassLoader,ExtClassLoader的父加载器是Bootstrap ClassLoader。
如果一个ClassLoader创建时如果没有指定parent,那么它的parent默认就是AppClassLoader。
四、类加载器的加载顺序
类加载器之间采用双亲委托的方式进行加载class。
一个类加载器查找class和resource时,是通过“委托模式”进行的。它首先判断这个class是不是已经加载,如果没有的话就交给父加载器进行查找是否已加载,父加载器如果也没有加载过,就交给更上层的类加载器查找,直至找到已加载这个class的类加载器,返回这个类,或者直到Bootstrap ClassLoader,如果Bootstrap classloader已加载,则直接返回,如果没加载就进行查找。如果没有找到,就交给下级加载器去查找加载,如果下级加载器没有加载到,就交给下级去加载,加载到即返回,直至最下层的加载器。
五、线程的contextClassLoader
1)contextClassLoader只是Thread类一个成员变量,通过setContextClassLoader()方法设置,通过getContextClassLoader()获取。
每个Thread都有一个相关联的ClassLoader,默认是AppClassLoader。并且子线程默认使用父线程的ClassLoader,除非子线程特别设置。
2)this.getClass.getClassLoader()和Thread.currentThread().getContextClassLoader()的区别
方法一得到的Classloader是静态的,表明类的载入者是谁;方法二得到的Classloader是动态的,谁执行(某个线程),就是那个执行者的Classloader,所以子线程默认使用父线程的ClassLoader。对于单例模式的类,静态类等,载入一次后,这个实例会被很多程序(线程)调用,对于这些类,载入的Classloader和执行线程的Classloader通常都不同。
六、获得ClassLoader的3种方法
this.getClass().getClassLoader(); // 使用当前类的ClassLoader。
Thread.currentThread().getContextClassLoader(); // 使用当前线程的ClassLoader。
ClassLoader.getSystemClassLoader(); // 使用系统ClassLoader,JVM下system ClassLoader通常为App ClassLoader。
七、class.getName()等方法
当你使用class.forName(),class.getName(),class.getResource()这几个不带ClassLoader参数的方法时,默认使用当前类的ClassLoader。
八、类加载器的getResource()方法
1.this.getClass().getResource("");//得到当前类class文件的URI目录。
2.this.getClass().getResource("/");//得到当前的classpath的绝对URI路径。
3.this.getClass().getClassLoader().getResource("");//得到当前ClassPath的绝对URI路径。
4.ClassLoader.getSystemResource("");//得到的当前ClassPath的绝对URI路径。
5.Thread.currentThread().getContextClassLoader().getResource("");得到当前ClassPath的绝对URI路径。
Class.getResourse()和Class.getClassLoader().getResource()
这两个getResource()是使用当前ClassLoader加载资源(即资源在classpath中),这样资源和class直接打在jar包中,避免文件路径问题。两者不同是Class的getResource()方法是从当前class文件路径查找资源,ClassLoader则是从jar包根目录查找。
九、用Classloader载入资源
所有资源都通过ClassLoader载入到JVM里,那么在载入资源时当然可以使用ClassLoader。只是对于不同的资源还可以使用一些别的方式载入,例如对于类可以直接new,对于文件可以直接做IO等。
1)实例化类B的三种方案
1. 使用Class静态方法 Class.forName
Class cls = Class.forName("com.rain.B");
B b = (B)cls.newInstance();
2. 使用ClassLoader
ClassLoader cl;//可以用前面提到的几种方法
Class cls = cl.loadClass("com.rain.B"); // 使用第一步得到的ClassLoader来载入B
B b = (B)cls.newInstance(); // 有B的类得到一个B的实例
3. 直接new
B b = new B();
2)文件载入(例如配置文件等)假设在com.rain.A类里想读取文件夹 /com/rain/config 里的文件sys.properties,读取文件可以通过绝对路径或相对路径,绝对路径很简单,在Windows下以盘号开始,在Unix下以"/"开始。对于相对路径,其相对值是相对于ClassLoader的,因为ClassLoader是一棵树,所以这个相对路径和ClassLoader树上的任何一个ClassLoader相对比较后可以找到文件,那么文件就可以找到。
1. 直接IO
File f = new File("C:/test/com/rain/config/sys.properties"); // 使用绝对路径
//File f = new File("com/rain/config/sys.properties"); // 使用相对路径
InputStream is = new FileInputStream(f);
2. 使用ClassLoader
因为有3种方法得到ClassLoader,对应有如下3种方法读取文件。 使用的路径是相对于这个ClassLoader的那个点的相对路径,此处只能使用相对路径
InputStream is = null;
is = this.getClass().getClassLoader().getResourceAsStream("com/rain/config/sys.properties"); //方法1
//is = Thread.currentThread().getContextClassLoader().getResourceAsStream("com/rain/config/sys.properties"); //方法2
//is = ClassLoader.getSystemResourceAsStream("com/rain/config/sys.properties"); //方法3