加载器
1. Bootstrap ClassLoader
负责加载$JAVA_HOME中jre/lib/rt.jar里所有的class,由C++实现,不是ClassLoader子类
2. Extension ClassLoader
负责加载java平台中扩展功能的一些jar包,包括$JAVA_HOME中jre/lib/*.jar或-Djava.ext.dirs指定目录下的jar包
3. App ClassLoader
负责记载classpath中指定的jar包及目录中class
4. Custom ClassLoader
属于应用程序根据自身需要自定义的ClassLoader,如tomcat、jboss都会根据j2ee规范自行实现ClassLoader
加载过程中会先检查类是否被已加载,检查顺序是自底向上,从Custom ClassLoader到BootStrap ClassLoader逐层检查,只要某个classloader已加载就视为已加载此类,保证此类只所有ClassLoader加载一次。而加载的顺序是自顶向下,也就是由上层来逐层尝试加载此类。
类的初始化
类什么时候才被初始化:
1)创建类的实例,也就是new一个对象
2)访问某个类或接口的静态变量,或者对该静态变量赋值
3)调用类的静态方法
4)反射(Class.forName("com.lyj.load"))
5)初始化一个类的子类(会首先初始化子类的父类)
6)JVM启动时标明的启动类,即文件名和类名相同的那个类
只有这6中情况才会导致类的类的初始化。
自定义加载器
public class CustomClassLoader extends ClassLoader {
// 要加载的类
private String name;
// 设置父加载器和需要加载的类
public CustomClassLoader(ClassLoader parent , String name) {
super(parent);
if(name == null || name.length() <= 0)
throw new NullPointerException();
this.name = name;
}
// 重写加载器
@Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
Class<?> clazz = null;
/**如果是我们想要热加载的类则调用我们重写的findClass方法来加载**/
if(this.name.equals(name) && !"java".equals(name)){
// 调用系统加载class文件
clazz = findLoadedClass(name);
if(clazz == null)
// 调用自定义的
clazz = findClass(name);
// 类的生命周期包括:加载、验证、准备、解析、初始化、使用、卸载。其中验证、准备、解析统称为连接
//如果类已连接过,resolveClass方法会直接返回
if(resolve)
resolveClass(clazz);
return clazz;
}
// 调用父加载器
return super.loadClass(name , resolve);
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
// 获得实际存储class文件位置
String fileName = c2f(name);
// 读取class文件
byte[] bytes = f2b(fileName);
// 根据class文件生成Class类对象
return defineClass(name, bytes, 0, bytes.length);
}
// 类名转为文件名
private String c2f(String name){
/**编译后的class文件存放的目录**/
String baseDir = "F:\\classes";
name = name.replace("." , File.separator);
name = baseDir + name + ".class";
return name;
}
// 读取文件byte数组
private byte[] f2b(String fileName){
RandomAccessFile file = null;
FileChannel channel = null;
byte[] bytes = null;
try {
/**随机存取文件对象,只读取模式**/
file = new RandomAccessFile(fileName , "r");
/**NIO文件通道**/
channel = file.getChannel();
/**NIO字节缓冲**/
ByteBuffer buffer = ByteBuffer.allocate(1024);
int size = (int) channel.size();
bytes = new byte[size];
int index = 0;
/**从NIO文件通道读取数据**/
while (channel.read(buffer) > 0){
/**字节缓冲从写模式转为读取模式**/
buffer.flip();
while (buffer.hasRemaining()){
bytes[index] = buffer.get();
++index;
}
/**字节缓冲的readerIndex、writerIndex置零**/
buffer.clear();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
if (channel != null) {
try {
channel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (file != null) {
try {
file.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return bytes;
}
// 加载
public Class<?> loadClass(){
try {
// 调用系统loadClass(name,false)
return loadClass(name);
} catch (ClassNotFoundException e) {
e.printStackTrace();
return null;
}
}
}
public static void main(String[] args) throws IOException, InstantiationException, IllegalAccessException {
String name = "top.xfdtm.test.Person";
CustomClassLoader loader = new CustomClassLoader(Thread.currentThread()
.getContextClassLoader(), name);
Class<?> clazz = loader.loadClass();
Object obj = clazz.newInstance();
}