本文已参与「新人创作礼」活动,一起开启掘金创作之路。
CLASS 加载过程
Loading
首先,类加载器通过类的全路径限定名称读取类的二进制字符流(获取二进制字符流) 其次,将字节流代表的静态存储结构化为方法去的运行时数据(结构化静态数据) 最后, 在堆生成一个代表这个类的class实例(不是这个类的实例)(在内存中生成class对象)
类加载器
启动类加载器/Bootstrap ClassLoader:负责加载JAVA_HOME/lib目录中的所有类
拓展类加载器/ExtClasLoader:负责加载 JAVA_HOME/lib/ext目录中的所有类
应用程序类加载器/AppClassLoader:负责加载用户类路径ClassPath下的所有类型
双亲委派
过程:当jvm收到类的加载请求时,会首先查询上一级加载器是否加载过这个类,若加载过,直接由上一级加载器返回结果,直到最顶层的bootstrap classloader。若都没有加载过,由最顶层开始判断是否可以加载这个类,如果加载器加载目录下找不到这个类,则会交给下一级加载器去加载 我们看下源码:
首先会判断是否被当前加载器加载了,加载了则返回;如果没有则会判断当前加载器是否有上级加载器,交由上级加载器加载。 优点:可以防止类的污染,一个类只会去被加载一次。 (历史上有4次java打破了双亲委派机制的事件,感兴趣的百度看看)
自定义类加载器
优点: 可以加载指定目录下的class文件。 可以参考一下下面这篇文章 www.jianshu.com/p/7ee1bfe63… 实现: 新建一个类继承自java.lang.ClassLoader,重写它的findClass方法。 将class字节码数组转换为Class类的实例 调用loadClass方法即可
public class MyClassLoader extends ClassLoader{
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
File f = new File("C:/.../.../"+name.replaceAll("\\.","/").concat(".class"));
try {
FileInputStream fis = new FileInputStream(f);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
int b = 0;
//数据写进字节数组
while ((b=fis.read())!=0){
byteArrayOutputStream.write(b);
}
byte[] bytes = byteArrayOutputStream.toByteArray();
byteArrayOutputStream.close();
fis.close();
return defineClass("name",bytes,0,bytes.length);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return super.findClass(name);
}
}
ClassLoader loader = new MyClassLoader();
Class clazz = loader.loadClass("com.....class全称");
Object o = clazz.newInstance();
linking
Verification
验证是连接阶段的第一步,这一阶段的目的是确保Class文件的字节流中包含的信息符合《Java虚拟机规范》的全部约束要求。 验证阶段大致分为四个阶段的动作,文件格式校验,元数据验证,字节码验证,符号引用验证 借张验证阶段的图: 原文在blog.csdn.net/weixin_4389…
Preparation
准备阶段是给静态变量分配内存并 设置类变量初始值 的阶段。
Resolution
解析阶段是Java虚拟机将常量池内的符号引用替换为直接引用的过程。 符号引用(Symbolic References):符号引用以一组符号来描述所引用的目标,符号可以是任何形式的字面量,只要使用时能无歧义地定位到目标即可。 直接引用(Direct References):直接引用是可以直接指向目标的指针、相对偏移量或者是一个能间接定位到目标的句柄。
initializing
这个阶段才真正开始执行java代码,静态代码块和设置变量的初始值为程序员设定的值