类加载器

2017/02/04 Java

类的加载

当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进行初始化。

  • 加载
    • 就是指将class文件读入内存,并为之创建一个Class对象。
    • 任何类被使用时系统都会建立一个Class对象。
  • 连接
    • 验证 是否有正确的内部结构,并和其他类协调一致
    • 准备 负责为类的静态成员分配内存,并设置默认初始化值
    • 解析 将类的二进制数据中的符号引用替换为直接引用
  • 初始化
    • 就是我们以前讲过的初始化步骤

类初始化时机

  • 创建类的实例
  • 访问类的静态变量,或者为静态变量赋值
  • 调用类的静态方法
  • 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
  • 初始化某个类的子类
  • 直接使用java.exe命令来运行某个主类

类加载器

负责将.class文件加载到内在中,并为之生成对应的Class对象。

类加载器的组成

  • Bootstrap ClassLoader 根类加载器
    • 也被称为引导类加载器,负责Java核心类的加载
      • 比如System,String等。在JDK中JRE的lib目录下rt.jar文件中
  • Extension ClassLoader 扩展类加载器
    • 负责JRE的扩展目录中jar包的加载。
      • 在JDK中JRE的lib目录下ext目录
  • Sysetm ClassLoader 系统类加载器
    • 负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径

类加载器之间的父子关系和管辖范围图

注意

  • 类加载器也是Java类,因为其他是java类的类加载器本身也要被类加载器加载,显然必须有第一个类加载器不是不是java类,这正是BootStrap。
  • Java虚拟机中的所有类装载器采用具有父子关系的树形结构进行组织,在实例化每个类装载器对象时,需要为其指定一个父级类装载器对象或者默认采用系统类装载器为其父级类加载。
ClassLoader loader = test.class.getClassLoader();
//打印出当前的类装载器,及该类装载器的各级父类装载器
while(loader != null)
{
	System.out.println(loader.getClass().getName());
	loader = loader.getParent();
}
// sun.misc.Launcher$AppClassLoader
// sun.misc.Launcher$ExtClassLoader

System.out.println(System.class.getClassLoader());// null 是由Bootstrap ClassLoader加载的

类加载器的委托机制

每个ClassLoader本身只能分别加载特定位置和目录中的类,但它们可以委托其他的类装载器去加载类,这就是类加载器的委托模式。
类装载器一级级委托到BootStrap类加载器,当BootStrap无法加载当前所要加载的类时,然后才一级级回退到子孙类装载器去进行真正的加载。当回退到最初的类装载器时,如果它自己也不能完成类的装载,那就应报告ClassNotFoundException异常。

当Java虚拟机要加载一个类时,到底派出哪个类加载器去加载呢?

  • 首先当前线程的类加载器去加载线程中的第一个类。
  • 如果类A中引用了类B,Java虚拟机将使用加载类A的类装载器来加载类B。
  • 还可以直接调用ClassLoader.loadClass()方法来指定某个类加载器去加载某个类。

自定义ClassLoader

Class clazz = new MyClassLoader("classDir").loadClass("cn.test.ClassLoaderAttachment");
Date d =  (Date)clazz.newInstance();
public class ClassLoaderAttachment extends Date {
	public String toString(){
		return "hello";
	} 
}
public class MyClassLoader extends ClassLoader{
	private String classDir;
	// findClass方法,实现当前子类加载器加载class方法,首先调用loadClass方法递归父类加载器加载后调用该方法
	@Override
	protected Class<?> findClass(String name) throws ClassNotFoundException {
		// TODO Auto-generated method stub
		String classFileName = classDir + "\\"  + name.substring(name.lastIndexOf('.')+1) + ".class";
		try {
			FileInputStream fis = new FileInputStream(classFileName);
			ByteArrayOutputStream  bos = new ByteArrayOutputStream();
			fis.close();
			byte[] bytes = bos.toByteArray();
			return defineClass(bytes, 0, bytes.length);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}
	
	public MyClassLoader(){
	}
	
	public MyClassLoader(String classDir){
		this.classDir = classDir;
	}
}

以上概念总结于传智播客Java基础课程

Search

    Post Directory