ClassLoader

什么是类加载器

java类加载器是通过一个类的全限定名来获取描述此类的二进制字节流,来进行加载;

加载器是在虚拟机外部实现的,方便让程序自己决定获取所需要的类。

JAVA有哪些类加载器

 对于开发人员来说,java的类加载器有四种,全都继承自抽象类java.lang.ClassLoader:

启动类加载器(Bootstrap ClassLoader)


它是虚拟机的一部分,无法被Java程序直接引用,所以System.class.getClassLoader()结果为null;

负责加载JAVA_HOMElib目录中规定的类库,例如rt.jar的java.lang.object等核心api。

扩展类加载器(Extension ClassLoader)


 负责加载<JAVA_HOME>libext目录,可以被开发人员直接使用。

程序类加载器(Application ClassLoader)


 程序默认的加载器,负责加载ClassPath下的类或jar,可以被开发人员直接使用。

自定义类加载器


自定义加载器需要继承ClassLoader,可以自己指定需要加载的类,以下是一个自定义加载器的demo:

package classLoader;

import java.io.IOException;
import java.io.InputStream;

/**
 * 自定义加载器测试类
 * 
 */
public class TestClassLoader extends ClassLoader{

    public static void main(String[] args) throws Exception{
        
        MyclassLoader myclassLoader = new MyclassLoader();
        AObj aObj = (AObj)myclassLoader.loadClass("classLoader.AObj").newInstance();
        aObj.sayHello();
    }
}


/**
 * 自定义加载器类(需继承ClassLoader)
 */
class MyclassLoader extends ClassLoader{

    
    /**
     * 自定义加载器有两种方式:
     *         重写findClass():此种方式遵守了双亲委派模式,符合java设计体系,推荐。
     *         重写loadClass():会破坏双亲委派模式,因为双亲委派的逻辑代码就在loadClass方法中,非不得已而为之。
     */
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        
        byte[] bt = loadClassData(name);
        
        //defineClass()是必须的,作用是通过class文件字节数组去获取Class对象
        return this.defineClass(name,bt,0,bt.length);
    }

    
    /**
     * 获取class的字节数组
     * @param name
     * @return
     */
    private byte[] loadClassData(String name) {
        
        String fileName = name.substring(name.lastIndexOf(".") + 1) + ".class";
        InputStream is = getClass().getResourceAsStream(fileName);
        byte[] bt = null;
        
        
        try {
            
            
            bt = new byte[is.available()];
            is.read(bt);
            
            
        } catch (IOException e) {
            
            e.printStackTrace();
            
        }finally{
            
            
            try {
                is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        
        return bt;
    }
}



/**
 * 用于测试自定义加载器的对象
 */
class AObj{
    
    public void sayHello() {
        
        System.out.println("Hello Word!");
    }
}
View Code

需要注意的是,即使用自定义加载器也无法加载像java.lang开头这样的类;

不过我并没有做此实验,因为实在是要去洗澡了。

类加载器之间的关系

启动类加载器 > 扩展类加载器 > 程序类加载器 >自定义类加载器:

它们之间不是继承关系,而是通过组合实现的,而这种模型也被称为双亲委派,如下图:

双亲委派模型

概念


双亲委派的代码逻辑在java.lang.ClassLoader.loadClass()中;

他是说某个特定的类加载器在接到加载类的请求时,首先将加载任务委托给父类加载器,依次递归;

如果父类加载器可以完成类加载任务,就成功返回;

只有父类加载器无法完成此加载任务时,才自己去加载。

为什么使用双亲委派


因为java的设计体系,比如继承,所有的类都必须继承object类,那就必须确定object是唯一的,否则我们怎么知道该继承哪个object;

而双亲委派会把加载类递归交给上一层加载器去做,如果加载不了,就自己完成,这一确保了类在系统中的唯一性。

原文地址:https://www.cnblogs.com/dahuandan/p/7112245.html