双亲委派模型和自定义类加载器

//可以参考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(); */  
     }  
}  

 

这个类加载器默认加载

 

[java]  view plain  copy
 
  print ?
  1. d:\myclass\com\wyp12\myClassLoader  

下面是要加载的类的源代码,很简答,就是让它打印出是谁加载了他

[html]  view plain  copy
 
  print ?
  1. package com.wyp12.myClassLoader;  
  2.   
  3. public class Dog {  
  4.     public Dog()  
  5.     {  
  6.         System.out.println(this.getClass().getClassLoader());  
  7.     }  
  8. }  


运行结果:

loader1

下面贴上ClassLoader的主要源代码:

它的几个构造函数

 

[html]  view plain  copy
 
  print ?
  1. protected ClassLoader() {  
  2.         this(checkCreateClassLoader(), getSystemClassLoader());  
  3.     }  
  4. protected ClassLoader(ClassLoader parent) {  
  5.         this(checkCreateClassLoader(), parent);  
  6.     }  
  7. protected ClassLoader() {  
  8.         this(checkCreateClassLoader(), getSystemClassLoader());  
  9.     }  


通过几个函数,把字节码数组转换成一个CLASS实例

[html]  view plain  copy
 
  print ?
  1.   protected final Class<?> defineClass(String name, byte[] b, int off, int len,  
  2.                      ProtectionDomain protectionDomain)  
  3.     throws ClassFormatError  
  4.     {  
  5.          return defineClassCond(name, b, off, len, protectionDomain, true);  
  6.     }  
  7.   
  8.     // Private method w/ an extra argument for skipping class verification  
  9.     private final Class<?> defineClassCond(String name,  
  10.                                            byte[] b, int off, int len,  
  11.                                            ProtectionDomain protectionDomain,  
  12.                                            boolean verify)  
  13.         throws ClassFormatError  
  14.     {  
  15.     protectionDomain = preDefineClass(name, protectionDomain);  
  16.   
  17.     Class c = null;  
  18.         String source = defineClassSourceLocation(protectionDomain);  
  19.   
  20.     try {  
  21.         c = defineClass1(name, b, off, len, protectionDomain, source,  
  22.                              verify);  
  23.     } catch (ClassFormatError cfe) {  
  24.         c = defineTransformedClass(name, b, off, len, protectionDomain, cfe,  
  25.                                        source, verify);  
  26.     }  
  27.   
  28.     postDefineClass(c, protectionDomain);  
  29.     return c;  
  30.     }  


下面这个是loadClass()

[html]  view plain  copy
 
  print ?
  1. public Class<?> loadClass(String name) throws ClassNotFoundException {  
  2.     return loadClass(name, false);  
  3.     }  
  4. protected synchronized Class<?> loadClass(String name, boolean resolve)  
  5.     throws ClassNotFoundException  
  6.     {  
  7.     // First, check if the class has already been loaded  
  8.     Class c = findLoadedClass(name);  
  9.     if (c == null) {  
  10.         try {  
  11.         if (parent != null) {  
  12.             c = parent.loadClass(name, false);  
  13.         } else {  
  14.             c = findBootstrapClassOrNull(name);  
  15.         }  
  16.         } catch (ClassNotFoundException e) {  
  17.                 // ClassNotFoundException thrown if class not found  
  18.                 // from the non-null parent class loader  
  19.             }  
  20.             if (c == null) {  
  21.             // If still not found, then invoke findClass in order  
  22.             // to find the class.  
  23.             c = findClass(name);  
  24.         }  
  25.     }  

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对象

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值