这是我参与「第四届青训营 」笔记创作活动的第3天
最近做安卓青训营的项目,对如此多的api给震惊到了,好奇java中类加载的机制到底是如何把海量的方法和类找到的。
一、概念
把描述类的数据从 Class 文件加载到内存
该过程包括了
- 加载
- 链接
- 初始化
类加载器的过程
类加载器的作用就是从文件系统或者网络中加载Class文件,至于他是否可以运行就不是ClassLoader的工作了。
1. 加载
类加载器(ClassLoader) 把class读入内存创建Class对象
2. 链接
把数据加载到JRE,给数据赋值
主要包括了
- 验证 --- 验证.class文件字节流是否符合class文件的格式的规范,验证class文件的魔术版本等
- 准备 --- 对Static变量设置默认值(不是初始值!!)
- 解析 --- 将类的二进制数据中的符号引用替换成直接引用
3. 初始化
- 初始化static(把准备阶段的static变量改成初始值)
- 假如这个类还没有被加载和连接,则程序先加载并连接该类
- 假如该类的直接父类还没有被初始化,则先初始化其直接父类
- 假如类中有初始化语句,则系统依次执行这些初始化语句
二、类加载的时机
- 初始化一个类时发现父类未加载
- new或反射调用的时候
- 调用静态方法或静态变量
三、3个类加载器ClassLoader
1. 启动类加载器
负责加载java核心类库(String等)
2. 扩展类加载器
加载jre中的jar
3. 应用程序类加载器
加载CLASSPATH路径下我们自己写的类
注意:getParent()获取到的父加载器!=父类
关键代码在Launcher.class下
四、类加载机制(双亲委派)
简述:属实是老生常谈了
三个步骤
- 查询缓存(已加载过则返回)
- 未加载过则请求父类去加载
- 父类加载失败则递归回来该类加载器查看是否能加载
好处:
优先使用父加载器,防止自己写个String去替换jdk的String类
例子: 看看ClassLoader下的loadClass方法,这个方法在子类多有重写
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// First, check if the class has already been loaded
//这里底层是调用了c++的搜索类,其实就是搜索缓存
Class<?> c = findLoadedClass(name);
if (c == null) {
if (parent != null) {
// 父加载器去加载
c = parent.loadClass(name, false);
} else {
// bootstrapclassloader是用c++写的无法使用getParent获取到
c = findBootstrapClassOrNull(name);
}
}
if (c == null) {
// 父类和缓存都没有加载,要自己去尝试加载了
c = findClass(name);
}
}
return c;
}
}
复制代码
五、自定义类加载器
在我们的日常应用程序的开发中,我基本是用不到自定义类加载器的,基本就是由前面介绍的这三个类加载器来相互配合搞定的。但是在有些特殊的情况下我们不希望通过系统的类加载器来处理,这时我们就可以通过自定义类加载来实现了。
用户自定义类加载器的实现步骤:
- 我们可以直接编写 java.lang.ClassLoader 类的实现来完成自定义的处理
- 在JDK1.2之前,自定义类加载器时我们总是会继承ClassLoader然后重写loadClass方法,达到自定义类加载的目的,但是在JDK1.2之后建议把自定义的类加载逻辑放在findClass()方法中
- 最后在编写自定义类加载的时候,如果没有太过于复杂的需求,可以直接继承URLClassLoader类,这样可以达到简化自定义类加载器的目的。
具体要怎么使用,使自己的容器具有特性,可以参考tomcat的类加载实现,防止了new出一个tomcat容器 具体可以拜读cloud.tencent.com/developer/a…