1.类加载器
- 虚拟机的设计团队把类加载阶段中的“通过一个类的全限定名来获取描述此类的二进制流”这个动作放到Java虚拟机外部去实现,以便让应用程序自己决定如何去获取所需要的类。实现这个动作的代码模块称之为类加载器。
- 只有被同一个类加载器加载的类才可能会相等。相同的字节码被不同的类加载器加载的类不相等。
2.类加载器分类
- 启动类加载器
由C++实现,是虚拟机的一部分,用于加载javahome下的lib目录下的类。
- 扩展类加载器
加载javahome下/lib/ext目录中的类。
- 应用程序类加载器
加载用户类路径上所指定的类库。
- 自定义类加载器
3.自定义类加载器
- 步骤
定义一个类,继承ClassLoader。
重写loadClass方法。
实例化Class对象。
- 优势
类加载器是Java语言的一项创新,也是Java语言流行的重要原因之一,它最新的设计是为了满足Java Applet的需求而开发出来的。
高度的灵活性。
通过自定义类加载器可以实现热部署。
代码加密。
- 示例代码
public class ClassLoaderDemo {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
MyClassLoader myClassLoader = new MyClassLoader();
Object c = myClassLoader.loadClass("com.my.classloader.ClassLoaderDemo").newInstance();
System.out.println(c.getClass());
// c由自定义类加载器加载,而ClassLoaderDemo由应用程序类加载器加载
// 是两个不同的类加载器,一个类由两个不同的了加载器加载,所以两个类不相等
System.out.println(c instanceof ClassLoaderDemo);
}
}
class MyClassLoader extends ClassLoader {
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
String fileName = name.substring(name.lastIndexOf(".") + 1) + ".class";
InputStream inputStream = getClass().getResourceAsStream(fileName);
if (inputStream == null) {
return super.loadClass(name);
}
try {
byte[] buffer = new byte[inputStream.available()];
inputStream.read(buffer);
return defineClass(name, buffer, 0, buffer.length);
} catch (Exception e) {
throw new ClassNotFoundException();
}
}
}
4.双亲委派模型
从JDK1.2开始,java虚拟机规范推荐开发者使用双亲委派模型进行类加载,其加载过程如下:
如果一个类加载器收到了类加载请求,它首先不会自己尝试加载这个类,而是把类加载请求委派给父类加载器去完成。每一层的类加载器都把类加载请求委派给父类加载器,直到所有的类加载请求都应该传递给顶层的启动类加载器。如果顶层的启动类加载器无法完成加载请求,子类加载器尝试去加载,如果连最初发类加载请求的类加载器也无法完成加载请求时,将会抛出ClassNotFoundException,而不再调用其子类加载器去进行类加载。双亲委派模型的类加载机制的优点是java类和它的类加载器一器具备了一种带优先级的层次关系,越是基础的类,越是被上层的类加载器进行加载,保证了java程序的稳定运行。