类加载器:
运行java程序时,首先运行JVM,再将java class加载到JVM中运行,负责加载java class的这部分就叫ClassLoader(把二进制文件识别、读取到内存中的功能模块)
JDK内置的三大类加载器:
- BootStrap:启动类加载器(加载object,String等基础类)
负责将存放于java_HOME\lib目录下的能被jvm识别的所有类库(例如rt.jar等java基础类库)加载到jvm中。使用C++语言实现,是jvm自身的一部分,独立于jvm外部,并且无法被jvm程序直接引用。 - ExtClassLoader:扩展类加载器
负责将存放于java_HOME\lib\ext(xml文件解析类、界面框架类)目录下的所有能被jvm识别的类库加载到jvm中。使用java语言实现,可以被java程序直接引用 - AppClassLoader:应用程序类加载器(加载用户classpath指定的类库)
使用java程序实现,如果用户没有自定义类加载器,则AppClassLoader就是程序中默认的类加载器。
class Loader{}
public class ClassLoaderDemo {
public static void main(String[] args) throws ClassNotFoundException {
Class cls = Class.forName("com.gitwh.Loader");
System.out.println(cls.getClassLoader());
System.out.println(cls.getClassLoader().getParent());
System.out.println(cls.getClassLoader().getParent().getParent());
}
}
可以看到不能获得BootStrap,为null
双亲委派模型:四种类加载器的层次关系称为双亲委派模型
工作流程:如果一个类加载器收到加载请求,首先自己不会去尝试加载此类,而是将类加载请求委托给父加载器完成,每一层类加载器都是如此。只有当父加载器无法加载此类时,子加载器才会尝试自己去加载。
存在意义:双亲委派模型对于保证java程序的稳定运行十分重要,保证类加载的安全性
作用:如果用户定义了与基础类库同名的类这时双亲委派模型就起作用了
以Object为例:自身定义的与基础类库同名的类会先交给顶层的BootStrap类加载器,但此时Object已经在这一层被加载了,不会扔回底端加载器AppCalssLoader,所以这个类不会被加载。这时就防止了用户定义的同名类(类全称)将原本存在的类冲击掉。
比较两个类相等的条件:这两个类是由同一个加载器加载的,否则即使两个类来自同一个Class文件,被同一个jvm加载,只要类加载器不同,就不相等(因为本质是class对象不相等)。
自定义类加载器:
自定义类加载器的两个重要步骤:
- 自定义类继承ClassLoader类
- 在自定义类中覆写findClass()方法
findClass方法的作用就是根据类名加载.Class字节码,
public Class<?> findClass(String className) throws ClassNotFoundException {
byte[] data = null;
try {
data = this.loadClassData();
} catch (Exception e) {
e.printStackTrace();
}
//将字节码转换为Class,返回Class对象
return super.defineClass(data,0,data.length);
}
defineClass()方法:将输入的byte数组装换为一个Class对象
protected final Class<?> defineClass(byte[] b, int off, int len)
throws ClassFormatError
{
return defineClass(null, b, off, len, null);
}
loadClassData()方法:获得.class字节码数组
public byte[] loadClassData() throws Exception{
InputStream in = new FileInputStream(
"C:\\Users\\xptyxx20170501A\\Desktop\\Test.class");
ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] data = new byte[20];
int temp = 0;
while ((temp = in.read(data))!=-1){
bos.write(data,0,temp);
}
byte[] result = bos.toByteArray();
in.close();
bos.close();
return result;
}
MyClassLoader myClassLoader = new MyClassLoader();
Class<?> cls = myClassLoader.findClass("com.gitwh.Test");
System.out.println(cls.getClassLoader());
System.out.println(cls.getClassLoader().getParent());