类加载器

虚拟机设计团队把类加载阶段中的获取二进制字节流这个动作放到Java虚拟机外部去实现,一遍让程序自己决定如何去获取所需要的类,实现这个动作的代码块被称为“类加载器”。
同一个Class文件如果被不同的类加载器所加载,那么加载出来的两个类是不相等的,所以某个Class文件只能由一个加载器加载。
这边的相等是Class兑现的equals()方法,isInstance()方法,isAssignableFrom()方法返回的结果。

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

public class ClassLoaderTest {
 
	public static void main(String[] args) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
		ClassLoader myLoader = new ClassLoader() {
			@Override
			public Class<?> loadClass(String name) throws ClassNotFoundException {
				try {
					String className = name.substring(name.lastIndexOf(".") + 1) + ".class";
					
					//返回读取指定资源的输入流
					InputStream is = getClass().getResourceAsStream(className);
					if (is == null) return super.loadClass(name);
					byte[] b = new byte[is.available()];
					is.read(b);
					
					//将一个byte数组转换为Class类的实例
					return defineClass(name, b, 0, b.length);
				} catch (IOException e) {
					throw new ClassNotFoundException(name);
				}
			}
		};
		
		Object object = myLoader.loadClass("ClassLoaderTest").newInstance();
		System.out.println(object.getClass());
		System.out.println(object instanceof ClassLoaderTest);
	}
}

以上代码运行结果如下图所示:
可以看到不同加载器说加载的类结果instanceof为false。
在这里插入图片描述

双亲委派模型

下面是双亲委派模型的实现,很明显可以看出,为了防止上面的不同类加载器加载同一个class文件的问题,双亲委派模型在加载Class文件的时候先问问其父类加载器有没有加载,所以,最先开始的是:

  • 启动类加载器(Bootstrap ClassLoader)这个加载器负责将存放在JAVA_HOME\bin目录下的包,或者被-Xbootclasspath参数所指定的路径中,并且是虚拟机识别的(即使是自己的包放到这个目录下,是不会被加载的)
  • 扩展类加载器(Extension ClassLoader):这个加载器负责JAVA_HOME\bin\ext目录中的,或者被java.ext.dirs系统变量所指定的路径中的所有类库,开发者可以直接使用扩展类加载器。
  • 应用程序类加载器(Application ClassLosder):这个加载器负责加载ClassPath上所指定的类库,

先检查是否被加载过,若没有加载则调用父类加载器的loadClass()方法,若父类加载器为空则默认使用启动类加载器作为父类加载器,如果父类加载器加载失败,抛出ClassNotFoundException异常后,再调用自己的findClass()方法进行加载。

protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
    //1 首先检查类是否被加载
    Class c = findLoadedClass(name);
    if (c == null) {
        try {
            if (parent != null) {
             //2 没有则调用父类加载器的loadClass()方法;
                c = parent.loadClass(name, false);
            } else {
            //3 若父类加载器为空,则默认使用启动类加载器作为父加载器;
                c = findBootstrapClass0(name);
            }
        } catch (ClassNotFoundException e) {
           //4 若父类加载失败,抛出ClassNotFoundException 异常后
            c = findClass(name);
        }
    }
    if (resolve) {
        //5 再调用自己的findClass() 方法。
        resolveClass(c);
    }
    return c;
}
Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐