一、类文件结构
① 魔数:确定这个文件是否为一个能被虚拟机接收的class文件。
② class文件版本:class文件的版本号,保证编译正常的执行
③ 常量池:存放两大常量(字面量和符号引用)
④ 访问标志:标志用于识别一些类或者接口层次的访问信息,即包括访问修饰符(public、private,protected,default)、类的类型(abstract,interface)、final等
⑤ 当前类索引,父类索引:类索引用于确定这个类的全限定类名,父类索引用于确定这个类的父类的全限定类名
⑥ 接口索引集合:接口索引集合用来描述类实现了哪些接口,实现的接口集合从左到右排列在索引集合中。
⑦ 字段表集合:描述接口或类中声明的变量。字段包括类级变量,但不包括在方法内部声明的局部变量
⑧ 方法表集合:类中的方法
⑨ 属性表集合:字段表、方法表都可以携带自己的属性集合
二、类加载过程
java编译器将.java文件编译成扩展名为.class的文件。.class文件中保存着java转换后,虚拟机要执行的指令,当需要某个类的时候,java虚拟机会加载.class文件,并创建对应的class对象,将class文件加载到虚拟机的内存,这个过程称为类的加载
类加载过程包括以下步骤:
加载->验证->准备->解析->初始化
1、加载:classLoader通过一个类的全限定类名查找此类字节码文件,并利用字节码文件创建一个class对象(.class文件)
2、验证:确保class文件中的字节流中包含信息符合当前虚拟机要求,不会危害虚拟机安全,主要包括:文件格式、元数据、字节码、符号引用的验证
3、准备:为类变量分配内存并且设置该变量的初始值,不会为实例变量分配初始值,类变量会分配到方法区中,实例变量会随着对象分配到java堆中。
4、解析:主要任务是把常量池中的符号引用替换为直接引用(下面解释)
5、初始化:如果有父类,先对父类进行初始化,执行其静态代码块,静态初始化成员,即进行静态代码块,构造代码块,构造方法等的加载
三、符号引用和直接引用
① 符号引用以一组符号来描述所引用的目标,符号引用可以是任何形式的字面量,只要 使用时能无歧义地定位到目标即可,符号引用于虚拟机的布局无关。
② 直接引用和虚拟机的布局是相关的,不同的虚拟机对于相同的符号引用所翻译出来的直接引用不一样,如果有了直接引用,直接引用的对象一定被加载到了内存中。直接引用可以是直接指向目标的指针,或者相对偏移量、一个间接定位到对象的句柄。
四、new一个对象的代码执行顺序
1、父类静态区域(由定义顺序从上到下)
① 为静态变量分配空间并赋予初始值(若静态代码块定义先与静态变量,则先加载静态代码块)
② 执行父类静态代码块
2、子类静态区域(由定义顺序从上到下)
① 为静态变量分配空间并赋予初始值
② 执行子类静态代码块
3、父类构造代码块
① 初始化父类非静态属性并赋值
② 执行父类构代码块
4、父类构造方法
① 执行父类构造方法
5、子类构造代码块
① 执行子类非静态属性并赋值
② 执行子类构造代码块
6、执行子类构造方法
①执行子类构造方法
总结:父类静态代码块 > 子类静态代码块 > 父类构造代码块 > 父类构造方法 > 子类静态代码块 > 子类构造方法
五、类加载机制(双亲委派模型)
1、类加载器:
① bootstrap classloder(启动类加载器)
② extension classloder(扩展类加载器)
③ Application classloder(应用程序类加载器)
④ User defined classloder(用户自定义类加载器)
2、双亲委派模型
当一个类进行加载的时候,会先判断当前类是否被加载过,已加载的类会直接返回,否则才会尝试加载,加载的时候,首先会把该任务委派给其父类加载器处理,若父类加载器没有加载过,再向上委派,直到请求到最高级父类加载器bootstrap classloder,当父类加载器无法处理时,才由自己来处理,当父类加载器为Null,会使用启动类加载器作为父类加载器
双亲委派模型避免了类的重复加载,两个不同的类加载器加载出来的对象是两个不同的对象,即使加载的是同一个类,JVM也会认为是不同的对象