Java 类加载器

一、类加载器


       类加载器(Class Loader)用来加载 Java 类到 Java 虚拟机中。一般来说,Java 虚拟机使用 Java 类的方式如下:Java 源程序(.java 文件)在经过 Java 编译器编译之后就转换成 Java 字节码(.class 文件)。类加载器负责读取 Java 字节码,并转换成 java.lang.Class 类的一个实例。每个这样的实例用来表示一个 Java 类。通过实例的 newInstance() 方法就可以创建出该类的一个对象。类加载器有是 Java 类,它也需要被加载器加载,显然必须有第一个不是 Java 类的类加载器—— BootStrap 加载。


二、类加载器的分类

  • 引导类加载器(BootStrap Class Loder——BootStrap):它用来加载 Java 核心库,是用 C++ 编写的一段二进制代码。
  • 扩展类加载器(Extensions Class Loder——ExtClassLoader):它用来加载 Java 的扩展库。Java 虚拟机会提供一个扩展库目录。该类加载器在此目录里面查找并加载 Java 类
  • 系统类加载器(System Class Loder——AppClassLoader):它根据 Java 应用的类路径(CLASSPATH)来加载 Java 类,一般来说 Java 应用的类都是由它来完成加载的。这就是为什么我们要把 .class 文件放在 CLASSPATH 里面的原因。


三、类加载器的层次关系


                Java 虚拟机中的所有类加载器采用具有父子关系的树形结构进行组织,在实例化每个类加载器对象时,需要为其指定一个父类加载器对象过着默认采用系统类加载器为其父级加载器。


                编程实例:演示类加载器的层次结构关系。

public class A {

	public static void main(String[] args) {

		ClassLoader classLoader = A.class.getClassLoader();
		while (classLoader != null) {	// 因为顶级加载器为 BootStrap 并不是 Java 类,所以在获取加载器对象时会是 null
			System.out.println(classLoader.getClass().getName()); // 获取当前类的加载器
			classLoader = classLoader.getParent(); // 当前类加载器的父级加载器
		}
	}
}
输出:

          sun.misc.Launcher$AppClassLoader
          sun.misc.Launcher$ExtClassLoader


四、类加载器的委托机制


                首先当线程的类加载器去加载线程中的第一个类,然后如果类 A 中引用了类 B ,Java 虚拟机将使用加载类 A 的加载器来加载类 B,还可以直接调用 ClassLoader.loadClass() 方法来指定某个类加载器去加载某个类。也就是说,类加载器在尝试自己去查找某个类的字节码文件时,会先委托给父级加载器先去尝试加载这个类,依次类推。这种模式又称为模版模式。当所有父级加载器并没有加载到该类时,会返回给发起者类加载器,如果仍然没有加载到,则会抛出 ClassNotFoundException 异常,不会再委托给子加载器。


五、Class.forName


                 Class.forName 是一个静态的方法,同样可以用来加载类。该方法有两种形式:Class.forName(String name, boolean initialize, ClassLoader loader) 和 Class.forName(String className)。第一种形式的参数 name 表示类的完全限定名,initialize 表示是否初始化类,loader 表示加载时使用的类加载器。第二种形式则相当于设置了参数 initialize 的值为 true,loader 的值为当前类的类加载器。Class.forName 是一个很常见的类加载方法,例如在获取 jdbc 驱动的时候。


六、自定义类加载器


       自定义加载器类必须继承 ClassLoader 类。在一般情况下只需要重写 ClassLoader 类中的 findClass() 方法


       编程实例:编写一个自己的类加载器,对已经加密的 .class 文件只能用我们自己写的类加载器加载。


      

public class A {

	public static void main(String[] args) throws Exception {

		String srcPath = args[0];
		String fileName = srcPath.substring(srcPath.lastIndexOf('\\') + 1);
		String desPath = args[1] + "\\" + fileName;
		FileInputStream fis = new FileInputStream(srcPath);
		FileOutputStream fos = new FileOutputStream(desPath);
		Cypher.cypher(fis, fos);
		fis.close();
		fos.close();

		// 用系统加载器加载,显示不能识别:Exception in thread "main"
		// java.lang.ClassFormatError: Incompatible magic value 889275713 in
		// class file ClassLoaderTest
		
		// Class newClass = A.class.getClassLoader().loadClass("ClassLoaderTest");

		// 用自定义加载器加载:输出 DriverKing_斌
		Class newClass = new MyClassLoader(desPath)
				.loadClass("ClassLoaderTest");
		Date date = (Date) newClass.newInstance();
		System.out.println(date.toString());
	}

}

class Cypher {
	public static void cypher(InputStream in, OutputStream out)
			throws Exception {
		int len = -1;
		while ((len = in.read()) != -1) {
			out.write(len ^ 0xff);
		}
	}
}

class MyClassLoader extends ClassLoader {

	private String filePath;

	public MyClassLoader() {
	}

	public MyClassLoader(String filePath) {
		this.filePath = filePath;
	}

	@SuppressWarnings("deprecation")
	protected Class<?> findClass(String name) throws ClassNotFoundException {
		try {
			FileInputStream fis = new FileInputStream(filePath);
			ByteArrayOutputStream bos = new ByteArrayOutputStream();
			Cypher.cypher(fis, bos);	//解密
			fis.close();
			byte[] bytes = bos.toByteArray();
			return defineClass(bytes, 0, bytes.length); // 返回字节码文件
		} catch (Exception e) {
			e.printStackTrace();
		}
		return super.findClass(name);
	}
}

class ClassLoaderTest extends Date {
	public String toString() {
		return "DriverKing_斌";
	}
}


搞了一个多小时!终于搞定了!!!!!淡定啊!!!!!


七、web 容器有关的类加载器


       由于我还没有对类加载器掌握透彻,还没有进行到这一步,继续学习中~!以后吃透了再添加进来





发布了54 篇原创文章 · 获赞 6 · 访问量 15万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章