class文件的加载主要分为3个步骤:
- Loading
- Linking (Verification -----> Preparation -----> Resolution)
- Initializing
Loading
Loading是指把class文件从硬盘加载到内存。
Linking
Linking(链接)又分为3小步:
① Verification(验证)主要做一些基础验证,包括文件是否符合class文件的标准等(模数验证)
② Preparation (准备)主要是给class文件中静态变量赋默认值
③ Resolution 将类、方法、属性等符号引用解析为直接引用,常量池中的各种符号引用解析为指针、偏移量等内存地址的直接引用。
Initializing
Initializing 初始化,把静态变量赋值为初始值(调用静态代码块)。
类加载器
classloader(类加载器)
class文件被加载到内存以后主要包括两块内容,class文件本身的二进制码以及该二进制码对应的Class对象。
java类加载器主要包括BootstarpClassLoader、ExtensionClassLoader、ApplicationClassLoader以及自定义类加载器。
BootstarpClassLoader(根加载器),由C++实现,加载jdk核心jar,例如lib/rt.jar、charset.jar等核心类,通过class.getClassLoader()方法获取到的值为null。
ExtensionClassLoader(扩展类记载器),主要加载扩展jar包,如jre/lib/ext/*.jar,也可以通过-Djava.ext.dirs指定。
ApplicationClassLoader(引用类加载器),加载classpath指定的内容(自己写的class文件)。
双亲委派
java的类加载机制被称为双亲委派,即一个class的加载首先由自定义类记载器(如果有的话)去自己的缓存中看看该类是否被加载,如果存在则证明该类已经被加载,如果不存在则委托自己的父加载器(application classLoader)尝试加载,父加载器可以通过getParent()方法获取。父加载器的加载过程也是一样,先确认自己的缓存中该类是否已经被加载,存在了这直接返回结果,不存在则委托自己的父加载器(extension classLoader)进行加载。如果在此过程中依然没有,则该加载类的请求会被委托到达bootstrap classLoader,根加载器依然是先查看自己是否加载过该类,如果没有则从固定路径进行加载,加载不成功则返回让extension classLoader尝试加载,extension classLoader加载不成功则通知application classLoader尝试加载,如果依然不成功,则由自定义类加载器进行加载,加载成功则返回结果,依然加载失败则抛出ClassNotFoundException。注意:各加载器之间不存在直接的继承关系,父加载器不是加载器的加载器,也不是类加载器的父类加载器。
之所以累的加载过程相对复杂,其原因在于通过双亲委派机制可以:
- 保证安全性。
- 保证一个类只会被加载一次。
自定义类加载器
自定义类加载器只需要extends ClassLoader覆盖findClass方法:
public class CustomClassLoader extends ClassLoader {
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
if (name == null || name.length() <= 0) {
return null;
}
String filePath = "D:\\code\\learn\\classloader\\target\\classes" + name.replace(".", "\\").concat(".class");
File file = new File(filePath);
try (FileInputStream in = new FileInputStream(file);
ByteArrayOutputStream out = new ByteArrayOutputStream()) {
int b;
while ((b = in.read()) != 0) {
out.write(b);
}
final byte[] bytes = out.toByteArray();
return defineClass(name, bytes, 0, bytes.length);
} catch (Exception e) {
e.printStackTrace();
}
return super.findClass(name);
}
public static void main(String[] args) throws Exception {
String name = "com.info.classloader.ClassExample";
CustomClassLoader classLoader = new CustomClassLoader();
final Class<?> aClass = classLoader.loadClass(name);
final ClassExample example = (ClassExample) aClass.newInstance();
example.method();
System.out.println(example.getClass().getClassLoader());
System.out.println(classLoader.getClass().getClassLoader());
System.out.println(classLoader.getClass().getClassLoader().getParent());
}
}
public class ClassExample {
public void method() {
System.out.println("method invoked....");
}
}