定义
虚拟机设计团队把类加载阶段中的”通过一个类的全限定名来获取描述此类的二进制字节流“这个动作放到Java虚拟机外部去实现,以便让应用程序自己决定如何去获取所需要的类。实现这个动作的代码模块称为”类加载器“
类加载器在类层次划分、OSGi、热部署、代码加密等领域大放异彩。
分类
类加载器的分类:
-
-
扩展类加载器:Extension ClassLoader,使用Java语言实现,负责加载<JAVA_HOME>/lib/ext目录下或被java.ext.dirs系统变量所指定的路径
-
应用程序类加载器:Application ClassLoader,使用Java语言实现,负责加载用户类路径(classpath),也称为系统类加载器
双亲委派
工作过程:如果一个类加载器收到了类加载请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到启动类加载器中,只有当父加载器反馈无法完成这个加载请求时,子加载器才会尝试自己去加载,
好处:Java类随着它的类加载器一起具备了一种带优先级的层次关系,保证了基础类的唯一性
三次被破坏:
-
JDK1.0中被覆盖的loadClass方法(JDK1.2之后不建议覆盖loadClass,而改用findClass方法,loadClass会在父类加载失败时调用findClass)
-
为了实现JNDI接口的服务提供者(SPI)引入了线程上下文类加载器(Thread Context ClassLoader),该类加载器如果未设置则从父线程继承,默认为应用程序类加载器
-
Java模块化,OSGi实现模块化热部署的关键则是它自定义的类加载机制的实现。每个程序模块(OSGi中称为Bundle)都有一个自己的类加载器,当需要更换一个Bundle时,就把Bundle连同类加载器一起还掉以实现代码的热替换
自定义类加载器示例
// /Users/frank/Documents/Develop/classes/com/yfsoft/java/model/HelloWorld.java package com.yfsoft.java.model; public class HelloWorld { public void hello() { System.out.println("Fuck You"); } } // 单独编译 // javac HelloWorld.java
package com.yfsoft.java; import org.apache.commons.io.FileUtils; import java.io.File; import java.io.IOException; import java.lang.reflect.InvocationTargetException; public class TestClassLoader { // 继承自ClassLoader private static class MyClassLoader extends ClassLoader { private String dir; public MyClassLoader(String dir) { this.dir = dir; } // 重写findClass方法,读取class文件内容并通过defineClass获取Class对象 @Override protected Class<?> findClass(String name) throws ClassNotFoundException { try { byte[] bytes = FileUtils.readFileToByteArray(new File(dir, name + ".class")); return defineClass(null, bytes, 0, bytes.length); } catch (IOException e) { throw new ClassNotFoundException("not class file found '" + name + ".class' in '" + dir + "' "); } } } public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException { MyClassLoader loader = new MyClassLoader("/Users/frank/Documents/Develop/classes/"); // 通过类的全限定名获取 // 一个类的全限定名是将类全名的"."全部换为"/" Class<?> cls = loader.loadClass("com/yfsoft/java/model/HelloWorld"); Object obj = cls.newInstance(); cls.getMethod("hello", new Class[0]).invoke(obj, new Object[0]); } }
-