//可以参考http://www.cnblogs.com/xrq730/p/4847337.html
类加载器:
- 1、根装载器(启动类装载器)
- 2、扩展类装载器
- 3、系统类装载器
- 4、用户自定义类加载器
rt.jar是JAVA基础类库,dt.jar是关于运行环境的类库,tools.jar是工具类库
设置在classpath里是为了让你 import *
web系统都用到tool.jar
双亲委派模型
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// First, check if the class has already been loaded
Class<?> c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {//父类加载器加载失败,抛出异常,然后调用自己的findclass
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
if (c == null) {
// 父类加载器无法加载时,调用自己的findclass来加载
// to find the class.
long t1 = System.nanoTime();
c = findClass(name);
// this is the defining class loader; record the stats
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {//如果要解析这个.class文件的话,就解析一下
resolveClass(c);
}
return c;
}
}
如果要解析这个.class文件的话,就解析一下
resolveClass(c);
}
return c;
}
}
要加载一个类的时候,首先叫负责加载该类的类装载器的父装载器去尝试加载,如果加载不了,再往上抛,一直抛到根加载器,如果根加载器还加载不了,那么就让负责加载该类的类装载器来加载。
自定义类加载器
protected Class<?> findClass(String name) throws ClassNotFoundException {
throw new ClassNotFoundException(name);
}
没有具体实现,只抛了一个异常,而且是protected的,这充分证明了:这个方法就是给开发者重写用的。
从上面对于java.lang.ClassLoader的loadClass(String name, boolean resolve)方法的解析来看,可以得出以下2个结论:
(1)如果不想打破双亲委派模型,那么只需要重写findClass方法即可
(2)如果想打破双亲委派模型,那么就重写整个loadClass方法
当然,我们自定义的ClassLoader不想打破双亲委派模型,所以自定义的ClassLoader继承自java.lang.ClassLoader并且只重写findClass方法。
步骤:
(1)有一个自定义的类
(2)自定义一个类加载器,里面主要是一些IO和NIO的内容,另外注意一下defineClass方法可以把二进制流字节组成的文件转换为一个java.lang.Class----只要二进制字节流的内容符合Class文件规范。我们自定义的MyClassLoader继承自java.lang.ClassLoader,就像上面说的,只实现findClass方法:
首先写一个自己的类加载器,MyClassLoader.java,继承ClassLoader类,然后重写findClass方法:
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
/**
* 自己写一个类加载器 ,去加载"d:\myclass\com\wyp12\*.class"
*
* @author prince
*
*/
public class MyClassLoader extends ClassLoader {
private String name;
public MyClassLoader(String name) {
super(); // 通过这个构造方法生成的类加载器,它的父加载器是系统类加载器
this.name = name;
}
public MyClassLoader(String name, ClassLoader loader) {
super(loader); // 通过这个这个构造方法生成的类加载器,该加载器的父加载器是loader,如果为空,则父加载器为根加载器
// 子类继承父类,如果不显式写出调用父类的哪个构造方法,那么就默认调用父类的无参构造函数
this.name = name;
}
public String toString()
{
return this.name;
}
// 要重写findclass这个方法,loadclass会调用它
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
// TODO Auto-generated method stub
byte[] data = null;
try {
FileInputStream fis = new FileInputStream("d:\\myclass\\com\\wyp12\\myClassLoader\\"+name+".class");
ByteArrayOutputStream abos = new ByteArrayOutputStream();
int ch = 0;
while (-1!=(ch=fis.read()))
{
abos.write(ch); //把字节一个一个写到输出流中
}
data = abos.toByteArray(); //把输出流中的字节弄成一个字节数组
}catch (Exception e) {
e.printStackTrace();
}
return this.defineClass("com.wyp12.myClassLoader."+name,data, 0, data.length,null);
}
public static void main(String[] args) throws Exception {
MyClassLoader l1 = new MyClassLoader("loader1");
Class dogC = l1.loadClass("Dog");
dogC.newInstance();
/*MyClassLoader l2 = new MyClassLoader("loader2",l1); //把L1作为它的父加载器
Class doccc = l2.loadClass("Dog");
doccc.newInstance(); */
}
}
这个类加载器默认加载
- d:\myclass\com\wyp12\myClassLoader
下面是要加载的类的源代码,很简答,就是让它打印出是谁加载了他
- package com.wyp12.myClassLoader;
- public class Dog {
- public Dog()
- {
- System.out.println(this.getClass().getClassLoader());
- }
- }
运行结果:
loader1
下面贴上ClassLoader的主要源代码:
它的几个构造函数
- protected ClassLoader() {
- this(checkCreateClassLoader(), getSystemClassLoader());
- }
- protected ClassLoader(ClassLoader parent) {
- this(checkCreateClassLoader(), parent);
- }
- protected ClassLoader() {
- this(checkCreateClassLoader(), getSystemClassLoader());
- }
通过几个函数,把字节码数组转换成一个CLASS实例
- protected final Class<?> defineClass(String name, byte[] b, int off, int len,
- ProtectionDomain protectionDomain)
- throws ClassFormatError
- {
- return defineClassCond(name, b, off, len, protectionDomain, true);
- }
- // Private method w/ an extra argument for skipping class verification
- private final Class<?> defineClassCond(String name,
- byte[] b, int off, int len,
- ProtectionDomain protectionDomain,
- boolean verify)
- throws ClassFormatError
- {
- protectionDomain = preDefineClass(name, protectionDomain);
- Class c = null;
- String source = defineClassSourceLocation(protectionDomain);
- try {
- c = defineClass1(name, b, off, len, protectionDomain, source,
- verify);
- } catch (ClassFormatError cfe) {
- c = defineTransformedClass(name, b, off, len, protectionDomain, cfe,
- source, verify);
- }
- postDefineClass(c, protectionDomain);
- return c;
- }
下面这个是loadClass()
- public Class<?> loadClass(String name) throws ClassNotFoundException {
- return loadClass(name, false);
- }
- 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);
- }
- }
Class.forName有一个三个参数的重载方法,可以指定类加载器,平时我们使用的Class.forName("XX.XX.XXX")都是使用的系统类加载器Application ClassLoader
public class TestMyClassLoader
{
public static void main(String[] args) throws Exception
{
MyClassLoader mcl = new MyClassLoader();
Class<?> c1 = Class.forName("com.xrq.classloader.Person", true, mcl);
Object obj = c1.newInstance();
System.out.println(obj);
System.out.println(obj.getClass().getClassLoader());
}
}
.class和getClass()的区别
它们二者都可以获取一个唯一的java.lang.Class对象,但是区别在于:
- 1、.class用于类名,getClass()是一个final native的方法,因此用于类实例
- 2、.class在编译期间就确定了一个类的java.lang.Class对象,但是getClass()方法在运行期间确定一个类实例的java.lang.Class对象