打印Thread.currentThread().getContextClassLoader(),显示如下:
sun.misc.Launcher$AppClassLoader@19821f这个加载器是系统类加载器。ClassLoader.getSystemResourceAsStream("com/config.xml")使用的就是系统类加载器定位资源的。
//JDK1.6,java.lang.ClassLoader的loadClass(String name, boolean resolve)方法的源码
protected synchronized Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
// First, check if the class has already been loaded
Class c = findLoadedClass(name);
if (c == null) {
try {
if (parent != null) {
//
c = parent.loadClass(name, false);
} else {
//
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
if (c == null) {
// If still not found, then invoke findClass in order to find the class.
//
c = findClass(name);
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
java中共有三种类型的类加载器:
1、引导(bootstrap)类加载器(用来加载java API类),例如加载java.lang.String类
2、扩展类加载器(就是sun.misc.Launcher$ExtClassLoader,用来加载jre\lib\ext目录下的jar包)
3、系统类加载器(就是sun.misc.Launcher$AppClassLoader,主要用来加载CLASSPATH设置目录中的Class)
创建一个URLClassLoader,发现其父加载器(parent,注意不是父类)的类型为sun.misc.Launcher$AppClassLoader,而sun.misc.Launcher$AppClassLoader和sun.misc.Launcher$ExtClassLoader的父类都是URLClassLoader。AppClassLoader的父加载器是ExtClassLoader,ExtClassLoader的父加载器为null,即bootstrap类加载器。
类加载有个双亲委托模式,
AppClassLoader的父加载器是ExtClassLoader ,ExtClassLoader 的父加载器是bootstrap classloader,bootstrap 是C++写的类加载器,会负责加载java核心类库,就是jre/lib/rt.jar
ExtClassLoader会加载扩展类库,就是jre/lib/ext下的库。
双亲委托模式就是子加载器会先委托父加载器加载,父加载器加载不了子加载器才加载,
这样做避免了重复加载,也加强了java的安全了,防止了恶意加载器去加载核心库。
String name = "com.domain.Account";
URL url1 = new URL("file:/D:/workspace/test/bin/");
ClassLoader cl = new URLClassLoader(new URL[] { url1 });
Class c1 = cl.loadClass(name);
URL url2 = new URL("file:/D:/workspace/test/bin");
ClassLoader cl2 = new URLClassLoader(new URL[] { url2 });
Class c2 = cl2.loadClass(name);
System.out.println(c1==c2);//返回true,原因是都是用系统类加载器AppClassLoader加载的
注意:
1,在类A中使用Class.forName加载类B,那么加载类A的类加载器将会用于加载类B,这样两个类的类加载器是同一个。
2,Class.forName("")和classLoader.load("")的区别主要是前者会做初始化,后者不会。见jdk注释:
A call to forName("X") causes the class named X to be initialized. 自己分别用两种方式装载一个带静态代码的类就知道了。jdbc需要通过Class.forName("")的方式来装载JDBC驱动程序(例如
Class.forName("com.mysql.jdbc.Driver"),之所以用Class.forName而没有用
ClassLoader.load(),就是因为需要JVM完成Driver的初始化工作,而不仅仅是装载),然后通过一个统一的工厂类
Java.sql.DriverManager来取得数据库连接,并执行各种操作。
Class.forName("")不仅load class而且还保证resolve这个class,包括常量池解析,类初始化。。。这样JDBC驱动使用这个方法,才能保证类里的静态方法执行,一般驱动类的静态方法会向DriverManager注册自己,如果用classloader.load("")就不一定会resolve这个class,也就不能保证注册驱动类!看了com.mysql.jdbc.Driver类的源码,静态代码就一句:java.sql.DriverManager.registerDriver(new com.mysql.jdbc.Driver())
3,
参考
1)java系统类加载器AppClassLoader之浅谈 http://blog.sina.com.cn/s/blog_4db6a3f101000do1.html
2)java类加载原理分析 http://gongmingwind.javaeye.com/blog/338366
3)解读ClassLoader http://www.javaeye.com/topic/83978
4)http://xyiyy.javaeye.com/blog/362107
Retrotranslator是一个Java字节码转换工具。它能够把用JDK5.0编译的Java Class转换成可运行在JVM1.4