一、加载阶段任务
the loading process basically performs these three functions:
- Create a binary stream of data from the class file通过类的全限定名来获取其定义的二进制字节流
- Parse the binary data according to the internal data structure将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构
- Create an instance of java.lang.Class在java堆中生成一个代表这个类的java.lang.Class对象,作为对方法区中这些数据的访问入口。
二、Java类加载器分类
- 启动类加载器(Bootstrap ClassLoader):负责将$JAVA_HOME中jre/lib目录下的或者被-Xbootclasspath参数所指定的路径中类库加载到虚拟机中。用C++实现。
- 拓展类加载器(Extention ClassLoader):负责将lib/ext或者由java.ext.dirs系统属性指定的目录中的JAR包的类库加载到虚拟机。用java实现
- 应用程序类加载器Application ClassLoader:负责将classpath或者项目中添加的jar包的类库加载到虚拟机。
//启动类加载器:
public static void bootClassLoaderLoadingPath(){
String b=System.getProperty("sun.boot.class.path");
//System.out.println(b);
List<String> bl= Arrays.asList(b.split(";"));
for(String str:bl)
{
System.out.println(str);
}
}
//拓展类加载器:
String b=System.getProperty("java.ext.dirs");
//应用程序类加载器:
String b=System.getProperty("java.class.path");
三、查看一个自定义类的类加载器
注:null代表启动类加载器
四、Parents Delegation Model(双亲委派模型)
The Java platform uses a delegation model for loading classes.
The basic idea is that every class loader has a "parent" class loader.
When loading a class, a class loader first "delegates" the search for
the class to its parent class loader before attempting to find the class itself.
Parents Delegation Model要求除了顶层的启动类加载器外,其余的加载器都应有自己的父类加载器。这些父加载器之间的父子关系不是通过继承的关系来实现,而是使用组合关系来复用父加载器的代码。
双亲委派模型的工作过程是:如果一个类加载器收到了类加载的请求,它首先不会自己 去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是 如此,因此所有的加载请求最终都应该传送到顶层的启动类加载器中,只有当父加载器反馈 自己无法完成这个加载请求(它的搜索范围中没有找到所需的类)时,子加载器才会尝试自 己去加载。(递归)
源码分析
代码解读:委派的代码都集中在java.lang.ClassLoader的loadClass()方法之中,代码 逻辑清晰易懂:先检查是否已经被加载过,若没有加载则调用父加载器的loadClass()方 法,若父加载器为空则默认使用启动类加载器作为父加载器。如果父类加载失败,抛出 ClassNotFoundException异常后,再调用自己的findClass()方法进行加载。
从上面源码分析也可以看出,一个类在加载时,首先要查看是否已经加载过,若加载过,则不会在加载此类。从而解释了在一个方法中new Person()多次只在第一次加载此类。
五、为什么要用Parents Delegation Model
- 避免重复加载:已经加载或父加载器已经加载了该类的时候,就没有必要子ClassLoader再加载一次。
- 层级关系:我们试想一下,如果不使用这种委托模式,那我们就可以随时使用自定义的String来动态替代java核心api中定义的类型,这样会存在非常大的安全隐患,而Parents Delegation Model的方式,就可以避免这种情况。
因为父加载器总有优先加载一个类的机会,当不同的child class loader试图记载一个更高级别的parent class loader加载范围的class时,该请求总会转发给相应的更高层级的父加载器,返回一致的结果。
六、两个class对象相等的必要条件
- 类的完整类名必须一致,包括包名。
- 加载这个类的ClassLoader(指ClassLoader实例对象)必须相同。
由两者共同保证class的唯一性,即使两个class是由同一份class字节码,在不同类加载器情况下也是不同的class。这句话可以表达得更通 俗一些:比较两个类是否“相等”,只有在这两个类是由同一个类加载器加载的前提下才有意义,否则,即使这两个类来源于同一个Class文件,被同一个虚拟机加载,只要加载它们的类 加载器不同,那这两个类就必定不相等。