如何加载class文件:
1.利用类加载器将将class文件读入到内存中,并且为这个class文件创建一个实例对象(java.lang.class),如果这个类已经被加载过了,就不会再被加载(同一个类的唯一标识:包名+类名)。
2.将类加载完之后,就是进行连接操作。
3.对类进行初始化。
接下来谈谈各个步骤的详细内容:
一.类的加载
利用不同的加载器,对不同来源的class文件进行加载,class文件有以下来源:
1,我们打好的jar包
2. 网络上的class文件
3.从本地文件系统加载的class
类加载器:
根类加载器(bootstrap classLoader):它的底层是采用c++代码写的,它其实不是classLoader的子类,它也没有子类,
因此它其实是不属于类的加载层级结构的,它完全由jvm来控制的,它负责加载的是java的核心库,比如说String,system这些核心库类。
扩展类加载器(extension classLoader):它负责加载的是jre的扩展目录(java.ext.dirs下的jar包),比如说咱们自己打的jar包。
应用类加载器(system ClassLoader):它负责加载java命令的-classpath选项和java.class.path系统的属性,它的父类是extentionclassLoader。
而extention classLoader和systemclassLoader都是继承了urlclassloader,urlclassloader的父类是classLoader,
因此层级关系大概如下:
用户类加载器->系统类加载器->扩展类加载器
jvm加载类的机制主要是如下几种:
(1)全盘负责:当一个类加载器加载某个class时,还会去加载它所依赖和引用的class,这些被依赖和引用的class文件其实就是隐式加载的方式。而主动加载的某个class是显示加载的方式。
(2)父类委托:先让父类加载器尝试加载此class文件,如果父类无法加载此class文件,则由当前类加载器去加载此clas文件。
(3)缓存机制:先从缓存区读取是否存在该class,如果没有,则利用加载器将其加载成class对象,然后存到缓存区中。
因此加载类的过程如下:
1.先在缓存区取class对象,如果不存在,则会递归请求父类加载器加载此class(用户类加载器->系统类加载器->扩展类加载器),如果父类加载器无法加载此class,则向下递归利用子类加载器加载此class,直到当前类加载器无法加载到class,抛出classnotfindexception,否则返回java.lang.class对象。
二.类的连接
1.验证:检查被加载的类是否有正确的内部结构,
2.准备:类加载器阶段负责对类的类变量分配内存,并且设置默认初始值
3.解析,将类的二进制数据的符号引用转化成直接引用。
三.类的初始化
初始化一个类时,假如直接父类没有初始化,则先初始化其直接父类,,假如类中有初始化语句,则先执行这些初始化语句,
因此类代码的执行顺序大概是这样子的
父类的静态代码块->子类的静态代码块->父类的初始化代码块(在创建对象时隐式执行,且在构造器前执行)->父类的构造函数->子类初始化代码块->子类构造函数
因此每次最先执行顶级的java.lang.object,jvm会保证这个类的所有父类被初始化(包括直接父类和间接父类)