Class类描述的整个类的信息,在Class类中提供的forName()方法是根据ClassPath配置的路径进行类的加载,日过现在需要加载的类的路径是文件或者网络,这个时候就需要实现类加载器,也就是ClassLoader起作用.
1.ClassLoader类加载器
定义如下
public ClassLoader getClassLoader()
然后编写一个简单的程序来观察一下ClassLoader的存在
package ClassLoader;
/**
* Author:weiwei
* description:ClassLoader类加载器
* Creat:2019/1/26
**/
class Member{}
public class ClassLoader1 {
public static void main(String[] args) {
Class<?> cls=Member.class;
System.out.println(cls.getClassLoader());
System.out.println(cls.getClassLoader().getParent());
System.out.println(cls.getClassLoader().getParent().getParent());
}
}
jdk.internal.loader.ClassLoaders$AppClassLoader@4629104a
jdk.internal.loader.ClassLoaders$PlatformClassLoader@f5f2bb7
null
运行结果如上所示,此时出现了两个类加载器,APPClassLoader(应用程序类加载器),PlatformClassLoader(平台类加载器),
那么,什么是类加载器呢?
类加载器基础概念
简单来说,Classloader 类加载器,用来加载Java类到 Java 虚拟机中的一种加载器。
用一张图了解一下ClassLoader
- 因为Java程序(class文件)与其他程序不同,不是本地可执行程序,当运行Java程序时,先运行JVM(Java虚拟机),然后再把Class类加载到JVM里面运行,负责加载Java Class的这部分就叫做ClassLoader(类加载器)
- JVM本身包含了一个ClassLoader叫做Bootstrap ClassLoader(启动类加载器),Bootstrap ClassLoader是使用C++实现的,无法被Java程序直接引用,它负责加载核心JavaClass(即所有Java.*开头的类)
- 另外JVM还提供了两个类加载器,ExtClassLoader(扩展类加载器),AppClassLoader(应用程序类加载器),它们都是由Java语言编写的,ExtClassLoader负责加载扩展的ClassLoader(例如Java.*开头的类和存放在JRE的ext目录下的类),开发者可直接使用扩展类加载器,APPClassLoader负责加载应用程序自身的类,如果应用程序中没有自定义自己的类加载器,则此加载器就是程序中默认的类加载器.
2.双亲委派模型
什么是双亲委派模型?
如上图所示,各类加载器之间的层次关系,就称为类加载器的双亲委派模型,双亲委派模型要求除了顶层的父类加载器外,其他的类加载器都应该有自己的父类加载器.
双亲委派模型的工作流程:如果一个类加载器收到了一个类加载请求,它首先不会自己尝试去加载这个类,而是把这个请求委托给父类加载器去完成,每一层次的类加载器都是如此,因此,所有的加载请求都应当传送到顶层的Bootstrap加载器中,只有当父类加载器反馈无法完成这个加载请求时(在自己的搜索范围内没有找到这个类),子加载器才会尝试自己去加载.
3.自定义类加载器
自定义类加载器:用户决定类从哪里加载
进行类加载的方法:
protected Class<?> loadClass(String name,boolean resolve)
throws ClassNotFoundException
自定义类加载器
package com.bittech.reflect.classloader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
/**
* Author: weiwei
* Created: 2019/1/27
* Description: 自定义类加载器
*/
public class MyClassLoader extends ClassLoader {
public Class<?> loadData(String name) throws ClassNotFoundException {
try {
byte[] classData = loadClassByte(name);
return this.defineClass(name, classData, 0, classData.length);
} catch (Exception e) {
e.printStackTrace();
}
return super.findClass(name);
}
//name类 -> .class
//.class文件通过文件读取的方式变成byte[]
private byte[] loadClassByte(String name) {
//classpath + classname
File file = new File("E:\\Member.class");
try (FileInputStream inputStream = new FileInputStream(file);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream()
) {
byte[] buff = new byte[1024];
int len = -1;
while ((len = inputStream.read(buff)) != -1) {
outputStream.write(buff, 0, len);
}
return outputStream.toByteArray();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
类加载器给用户提供的最大的帮助是:可以通过动态的路径进行类的加载操作
比较两个类相等的前提是:必须由同一个类加载器锁加载,否则即使两个类来源于同一个Class文件,被同一个虚拟机加载,只要加载它们的类加载器不同,那么这两个类注定不相等.