lassLoader主要对类的请求提供服务,当JVM须要某类时,它根据名称向ClassLoader要求这个类,而后由ClassLoader返回这个类的class对象。html
ClassLoader负责载入系统的全部资源(Class,文件,图片,来自网络的字节流等),java
经过ClassLoader从而将资源载入JVM 中。每一个class都有一个引用,指向本身的ClassLoader。web
能够经过以下3种方法获得ClassLoader :网络
this.getClass.getClassLoader(); // 使用当前类的ClassLoader Thread.currentThread().getContextClassLoader(); // 使用当前线程的ClassLoader ClassLoader.getSystemClassLoader(); // 使用系统ClassLoader,即系统的入口点所使用的ClassLoader。
注:system ClassLoader与根ClassLoader并不同。JVM下system ClassLoader一般为App ClassLoader。ui
全部资源都经过ClassLoader载入到JVM里,那么在载入资源时固然可使用ClassLoader,只是对于不一样的资源还可使用一些别的方式载入,例如对于类能够直接new,对于文件能够直接作IO等。this
假设有类A和类B,A在其方法里须要实例化B,载入类可能的方法有3种。对于载入类的状况,用户须要知道B类的完整名字(包括包名,例如"com.alexia.B") 编码
Class cls = Class.forName("com.alexia.B");
B b = (B)cls.newInstance();
/* Step 1. Get ClassLoader */ ClassLoader cl = this.getClass.getClassLoader();; // 如何得到ClassLoader参考1 /* Step 2. Load the class */ Class cls = cl.loadClass("com.alexia.B"); // 使用第一步获得的ClassLoader来载入B /* Step 3. new instance */ B b = (B)cls.newInstance(); // 有B的类获得一个B的实例
B b = new B();
注:有人内心可能会想,对于类的载入方式咱们都会选择最简单的第3种方式,前两种方式彻底是多余。spa
实则否则,直接new的方式也是有局限的,举个最简单的例子:Java中有包名的类怎么引用默认包中的类?.net
固然说这个是由于有包名的类不能直接用new引用默认包中的类,那么怎么办呢?答案是使用反射机制,即便用第一种方式来加载类(具体请看这里)。线程
并且,用new()和用newInstance()建立类的实例是不一样的,主要区别简单描述以下:
从JVM的角度看,咱们使用关键字new建立一个类的时候,这个类能够没有被加载。可是使用newInstance()方法的时候,就必须保证:
(1)这个类已经加载;
(2)这个类已经连接了(即为静态域分配存储空间,而且若是必须的话将解析这个类建立的对其余类的全部引用)。
而完成上面两个步骤的正是Class的静态方法forName()所完成的,这个静态方法调用了启动类加载器,即加载javaAPI的那个加载器。
能够看出,newInstance()其实是把new这个方式分解为两步,即首先调用Class加载方法加载某个类,而后实例化。
这样分步的好处是显而易见的。咱们能够在调用class的静态加载方法forName时得到更好的灵活性,提供给了一种降耦的手段。
假设在com.alexia.A类里想读取文件夹 /com/alexia/config 里的文件sys.properties,读取文件能够经过绝对路径或相对路径,绝对路径很简单,
在Windows下以盘号开始,在Unix下以"/"开始。对于相对路径,其相对值是相对于ClassLoader的,由于ClassLoader是一棵树,
因此这个相对路径和ClassLoader树上的任何一个ClassLoader相对比较后能够找到文件,那么文件就能够找到。文件有如下三种加载方式:
/** * 假设当前位置是 "C:/test",经过执行以下命令来运行A "java com.aleixa.A" * 1. 在程序里可使用绝对路径,Windows下的绝对路径以盘号开始,Unix下以"/"开始 * 2. 也可使用相对路径,相对路径前面没有"/" * 由于咱们在 "C:/test" 目录下执行程序,程序入口点是"C:/test",相对路径就 * 是 "com/alexia/config/sys.properties" * (例子中,当前程序的ClassLoader是App ClassLoader,system ClassLoader = 当前的 * 程序的ClassLoader,入口点是"C:/test") * 对于ClassLoader树,若是文件在jdk lib下,或在jdk lib/ext下,或在环境变量里, * 均可以经过相对路径"sys.properties"找到,lib下的文件最早被找到 */ File f = new File("C:/test/com/aleixa/config/sys.properties"); // 使用绝对路径 //File f = new File("com/alexia/config/sys.properties"); // 使用相对路径 InputStream is = new FileInputStream(f);
/** * 由于有3种方法获得ClassLoader,对应有以下3种ClassLoader方法读取文件 * 使用的路径是相对于这个ClassLoader的那个点的相对路径,此处只能使用相对路径 */ InputStream is = null; is = this.getClass().getClassLoader().getResourceAsStream( "com/alexia/config/sys.properties"); //方法1 //is = Thread.currentThread().getContextClassLoader().getResourceAsStream( "com/alexia/config/sys.properties"); //方法2 //is = ClassLoader.getSystemResourceAsStream("com/alexia/config/sys.properties"); //方法3
ResourceBundle bundle = ResourceBundle.getBoundle("com.alexia.config.sys");
这种用法一般用来载入用户的配置文件,关于ResourceBunlde更详细的用法请参考其余文档。
注:若是是属性配置文件,也能够经过java.util.Properties.load(is)将内容读到Properties里,Properties默认认为is的编码是ISO-8859-1,若是配置文件是非英文的,可能出现乱码问题。
总结:有以下3种途径来载入文件
1. 绝对路径 ---> IO
2. 相对路径 ---> IO
---> ClassLoader
3. 资源捆绑 ---> ResourceBundle
在web应用里固然也可使用ClassLoader来载入资源,但更经常使用的状况是使用ServletContext,以下是web目录结构
ContextRoot
|- JSP、HTML、Image等各类文件
|- [WEB-INF]
|- web.xml
|- [lib] Web用到的JAR文件
|- [classes] 类文件
用户程序一般在classes目录下,若是想读取classes目录里的文件,可使用ClassLoader,若是想读取其余的文件,通常使用ServletContext.getResource()。
若是使用ServletContext.getResource(path)方法,路径必须以"/"开始,路径被解释成相对于ContextRoot的路径,此处载入文件的方法和ClassLoader不一样,举例"/WEB-INF/web.xml","/download/WebExAgent.rar"
参考:http://blog.chinaunix.net/uid-21227800-id-65885.html