java引入字节码和JVM机制,提供了强大的跨平台能力。理解Java的类加载机制是深入开发java的必要条件。
今天主要介绍我理解的类加载过程和双亲委派模型。
类加载过程:
类的加载主要分为三个步骤:加载,链接,初始化。
加载过程:将Java字节码数据源读取到JVM中。并映射为jVM可以读懂的结构(类对象)。数据源:jar文件,class文件,网络数据源等,如果输入的不是classFile则抛出异常。
加载是通过加载器去加载:分为bootstrap加载器,扩展,应用加载器,以及用户自己可以定义用户自己的类加载器。(用户可以参与)
Bootstrap ClassLoader 负责加载java基础类,主要是 %JRE_HOME/lib/ 目录下的rt.jar、resources.jar、charsets.jar和class等
Extension ClassLoader 负责加载java扩展类,主要是 %JRE_HOME/lib/ext 目录下的jar和class
App ClassLoader 负责加载当前java应用的classpath中的所有类。
其中Bootstrap ClassLoader是JVM级别的,由C++撰写;Extension ClassLoader、App ClassLoader都是java类,都继承自URLClassLoader超类。
Bootstrap ClassLoader由JVM启动,然后初始化sun.misc.Launcher ,sun.misc.Launcher初始化Extension ClassLoader、App ClassLoader。
自定义的类加载器:
private String path = "D:/user/classpath";
public UserClassLoader(ClassLoader parent, String path) {
super(parent);
this.path = path;
}
@Override
public Class<?> loadClass(String name) {
if(name.startsWith("java.")){
try {
return super.loadClass(name, false);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
byte[] data = loadClassData(name);
if(data != null)
return defineClass(name, data, 0, data.length);
else{
return null;
}
}
/**
* 根据文件名加载数据
* @param name
* @return
*/
private byte[] loadClassData(String name) {
FileInputStream is = null;
ByteArrayOutputStream bos = null;
try {
name = name.replace(".", "//");
File file = new File(path+"/"+name+".class");
is = new FileInputStream(file);
bos = new ByteArrayOutputStream();
int b ;
while((b = is.read())!=-1) {
bos.write(b);
}
return bos.toByteArray();
}catch(Exception e){
}
finally {
try {
is.close();
bos.close();
}catch(Exception e) {
}
}
return null;
}
public static void main(String[] args) throws Exception, IllegalAccessException {
UserClassLoader ul = new UserClassLoader(null,"D:");
Class<?> al = ul.loadClass("com.zxh.java8.test.loader.Animal");
Method say = al.getMethod("say", null);
Object alin = al.newInstance();
say.invoke(alin, null);
}
链接过程:核心步骤,将类定义的信息转化成JVM运行需要的的过程中,可以细分为:
1.验证,包括字节信息是否JVM规范,防止破坏JVM
2.准备,创建类或者接口中的静态变量,并初始化静态变量中的值
3.解析,将常量池中的符号引用替换成直接引用
初始化过程:真正执行初始化的代码逻辑,包括静态字段赋值以及执行静态初始化快的内部逻辑,父类初始化逻辑优先
双亲委派:当类加载器试图加载某个类型的时候,除非父类找不到相应类型,否则尽量让这个任务代理给当前加载的父加载器去做。目的避免重复加载java 类型。