作用
- 编译器先把java文件编译成class字节码文件
- ClassLoad会把字节码文件转换成jvm中的Class对象
- jvm再把class对象转成实例对象
ClassLoad在java 中有着非常重要的作用,他主要工作是在Class装载的加载过程,他的主要作用是 从系统外部获取二进制数据流。它是java的核心组件,所有的class都是通过ClassLoad进行加载的,ClassLoad负责将class文件里的二进制流输入到系统中交给jvm进行连接,初始化操作。
分类
- 系统自带的类加载器
- 启动类加载器(BootStrap) c++编写 加载核心库 java.* 3.拓展类加载器 (Extension) java编写 加载拓展库 javax.* 4.应用程序加载类(AppClassLoader)java编写 加载程序所在目录 5.用户自定义的类加载器 自定义类加载器 java编写 定制化加载
我们先了解2个方法
第一个方法 findClass
/**
* Finds the class with the specified <a href="#name">binary name</a>.
* This method should be overridden by class loader implementations that
* follow the delegation model for loading classes, and will be invoked by
* the {@link #loadClass <tt>loadClass</tt>} method after checking the
* parent class loader for the requested class. The default implementation
* throws a <tt>ClassNotFoundException</tt>.
*
* @param name
* The <a href="#name">binary name</a> of the class
*
* @return The resulting <tt>Class</tt> object
*
* @throws ClassNotFoundException
* If the class could not be found
*
* @since 1.2
*/
protected Class<?> findClass(String name) throws ClassNotFoundException {
throw new ClassNotFoundException(name);
}
我大概翻译了一下:
将字节数组转换为类Class的实例。必须先解析Class才能使用。
大白话就是,将字节流转换为类,找不到就报错!子类应该覆盖这个方法
第二个方法 defineClass
protected final Class<?> defineClass(String name, byte[] b, int off, int len,
ProtectionDomain protectionDomain)
throws ClassFormatError
{
protectionDomain = preDefineClass(name, protectionDomain);
String source = defineClassSourceLocation(protectionDomain);
Class<?> c = defineClass1(name, b, off, len, protectionDomain, source);
postDefineClass(c, protectionDomain);
return c;
}
根据其官方文档,再简化之后我总结了九个字:将字节数组转换为类
模拟一个类,假装这个类是外部文件!我们通过类加载器去加载这个文件!
我们通过类加载器创建该实例!他就会打印构造函数中的一句话!废话不多说我们直接上图吧!
/**
* 模拟这个类被加载,我们先把他搞成.class文件 然后,他被加载时就会打印空构造方法!
*/
public class TestClasssLoader {
public TestClasssLoader() {
System.out.println("Hello ClasssLoader!!!");
}
}
在本地cmd命令行,通过javac TestClasssLoader.java 编译此文件,会得到一个.class文件,我们将这个class文件随便放个地方从!
然后开始编写我们自己的类加载器!
package com.zanzan.vo;
import lombok.AllArgsConstructor;
import java.io.*;
/**
* 自定义类加载器
* @author huangfu
* @AllArgsConstructor 是lombok一个插件,他就是生成MyClassLoader的构造方法的!你也可以手动生成
*/
@AllArgsConstructor
public class MyClassLoader extends ClassLoader {
/**
* .class文件所在位置
*/
private String path;
/**
* 查找类加载器
* @param name 类名(不带.class)
* @return
* @throws ClassNotFoundException
*/
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
byte[] bytes = new byte[0];
try {
//这里就是为了获取.class文件的字节流
bytes = loadClassFile(name);
} catch (IOException e) {
e.printStackTrace();
}
/**
* 开始加载类
* 四个参数!
* 1.类的名称
* 2. .class文件的字节流
* 3. 从那个位置开始读取
* 4. 读取多长
*/
return defineClass(name,bytes,0,bytes.length);
}
/**
* 获取.class文件的字节流信息
* @param fileName
* @return
* @throws IOException
*/
private byte[] loadClassFile(String fileName) throws IOException {
String pathFile = path+File.separator+fileName+".class";
FileInputStream in = new FileInputStream(pathFile);
ByteArrayOutputStream out = new ByteArrayOutputStream();
int readIndex = -1;
while ((readIndex = in.read())!=-1){
out.write(readIndex);
}
in.close();
out.close();
return out.toByteArray();
}
}
功能测试
终于,我们的代码写完了,我们需要测试一下功能!在测试之前回顾一下java的一个基础!类在被加载的时候,会自动调用空构造函数!所以我们的外部类,一旦被加载就会打印 Hello ClasssLoader!!!
来吧,试一下吧!
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
MyClassLoader myClassLoader = new MyClassLoader("C:\\Users\\huangfu\\Desktop");
Class<?> testClasssLoader = myClassLoader.findClass("TestClasssLoader");
testClasssLoader.newInstance();
}
结果:
Hello ClassLoader!!!
Process finished with exit code 0
搞定收工!赶紧回去试试吧!
欢迎关注作者哦!