类文件结构
ClassFile {
u4 magic; //Class ⽂件的标志
u2 minor_version;//Class 的⼩版本号
u2 major_version;//Class 的⼤版本号
u2 constant_pool_count;//常量池的数量
cp_info constant_pool[constant_pool_count-1];//常量池
u2 access_flags;//Class 的访问标记
u2 this_class;//当前类
u2 super_class;//⽗类
u2 interfaces_count;//接⼝
u2 interfaces[interfaces_count];//⼀个类可以实现多个接⼝
u2 fields_count;//Class ⽂件的字段属性
field_info fields[fields_count];//⼀个类会可以有个字段
u2 methods_count;//Class ⽂件的⽅法数量
method_info methods[methods_count];//⼀个类可以有个多个⽅法
u2 attributes_count;//此类的属性表中的属性数
attribute_info attributes[attributes_count];//属性表集合
}
- 魔数: 确定这个⽂件是否为⼀个能被虚拟机接收的 Class ⽂件。
- Class ⽂件版本 : Class ⽂件的版本号,保证编译正常执⾏。
- 常量池 :常量池主要存放两⼤常量:字⾯量和符号引⽤。
- 访问标志 :标志⽤于识别⼀些类或者接⼝层次的访问信息,包括:这个 Class 是类还是接⼝,是否为 public 或者 abstract 类型,如果是类的话是否声明为 final 等等。
- 当前类索引,⽗类索引 :类索引⽤于确定这个类的全限定名,⽗类索引⽤于确定这个类的⽗类的全限定名,由于 Java 语⾔的单继承,所以⽗类索引只有⼀个,除了 java.lang.Object 之外,所有的 java 类都有⽗类,因此除了 java.lang.Object 外,所有 Java 类的⽗类索引都不为 0。索引对应的值在常量池。
- 接⼝索引集合 :接⼝索引集合⽤来描述这个类实现了那些接⼝,这些被实现的接⼝将按 implents (如果这个类本身是接⼝的话则是 extends ) 后的接⼝顺序从左到右排列在接⼝索引集合中。索引对应的值在常量池。
- 字段表集合 :描述接⼝或类中声明的变量。字段包括类级变量以及实例变量,但不包括在⽅法内部声明的局部变量。
- ⽅法表集合 :类中的⽅法。
- 属性表集合 : 在 Class ⽂件,字段表,⽅法表中都可以携带⾃⼰的属性表集合。
类加载机制
类加载过程
一个类型从被加载到虚拟机内存中开始, 到卸载出内存为止, 它的整个生命周期将会经历加载(Loading) 、 验证(Verification) 、 准备(Preparation) 、 解析(Resolution) 、 初始化(Initialization) 、 使用(Using) 和卸载(Unloading) 七个阶段, 其中验证、 准备、 解析三个部分统称为连接(Linking) 。
加载:
1) 通过一个类的全限定名来获取定义此类的二进制字节流。
2) 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。
3) 在Java堆内存中实例化一个java.lang.Class类的对象,这个对象将作为程序访问方法区中的类型数据的外部接口。
验证:分为文件格式验证,元数据验证,字节码验证,符号引用验证(发生在解析阶段),主要验证该class文件中的字节流信息复合虚拟机的要求,以及不会威胁到jvm的安全;
准备:为class对象的静态变量分配内存,初始化其初始值;
解析:该阶段主要完成符号引用转化成直接引用;
初始化:开始执行类中定义的java代码,初始化阶段是调用类构造器的过程;
什么是双亲委派模型?
当一个类加载器收到一个类加载的请求,他首先不会尝试自己去加载,而是将这个请求委派给父类加载器去加载,只有父类加载器在自己的搜索范围类查找不到给类时,子加载器才会尝试自己去加载该类;
为什么需要双亲委派模型?
因为不同的类加载器,即使加载同一个类也是不相等的。为了防止内存中出现多个相同的字节码,保证类的唯一性,就需要双亲委派模型使所有类的加载器是同一个(启动类加载器)。
怎么打破双亲委派模型?
自定义类加载器,继承ClassLoader类,重写loadClass方法和findClass方法;