类文件结构
ClassFile {
u4 magic;
u2 minor_version;
u2 major_version;
u2 constant_pool_count; //常量池信息
cp_info constant_pool[constant_pool_count-1]; //常量池信息
u2 access_flags; //访问修饰符
u2 this_class;
u2 super_class;
u2 interfaces_count; //接口信息
u2 interfaces[interfaces_count]; //接口信息
u2 fields_count; // 类中成员变量
field_info fields[fields_count]; //类中成员变量
u2 methods_count; //类中方法
method_info methods [methods_count]; //类中方法信息
u2 attributes_count; //类的附加属性
attribute_info attributes[attributes_count]; //类的附加属性
}
magic
0 - 3字节,表示它是否是class类型文件
版本
4 - 7 字节,表示类的版本,52表示Java 8
常量池
8 - 9 字节,表示常量池长度
字节码指令
- javac 将java文件编译为字节码文件
- javap -v 反编译class文件
异常
- 实现原理是异常表
语法糖
- 自动生成默认构造参数
- 泛型被优化为Object的形式
- 自动拆装箱是代码优化的形式(不用显示转换)
- 可变参数被优化为数组的形式
- foreach对数组循环被编译器优化为fori循环
- foreach对集合循环被优化为迭代器循环(集合接口继承了迭代器接口)
- switch结合string分为两阶段switch
– 首先判断 hashcode,然后判断string是否相等,对byte复制
– byte 不同的值对应不同的执行
– hashcode 相同时,增加 if 判断 - 枚举配合switch是优化为了数组
编译期处理
类加载阶段
加载
连接 - 验证
- 判断文件是否是java的class文件
连接 - 准备
- 为 static 修饰的变量申请空间
- 为 final static 修饰的基本类型,字符串常量申请空间,赋值
- 未被static final 修饰的变量在构造器中申请空间,赋值
连接 - 解析
- 将常量池中的符号引用解析为直接引用
初始化
- 调用构造方法,虚拟机会保证执行过程的线程安全
发生时机
类初始化是懒惰的
- main方法所在的类,首先被初始化
- 首次访问这个类的静态变量和静态方法时
- 子类初始化,如果父类还没初始化,会引发
- 子类访问父类的静态变量,只会触发父类的初始化
- Class.forname
- new 会导致初始化
不会导致类初始化的情况 - 访问类的 static final 静态变量 (基本类型和字符串)不会触发初始化
- 类对象的.class 不会触发初始化
- 类加载器的loadClass方法
- Class.forname的参数2为false时
类加载器
- Bootstrap ClassLoader,无法直接访问,加载JAVA_HOME/jre/lib下的类
- Extension ClassLoader,上级为Bootstrap ClassLoader,显示为null,加载JAVA_HOME/jre/lib/ext下的类
- Application ClassLoader,上级为Extension ClassLoader,加载classpath下的类
- 自定义ClassLoader, 上级为Application ClassLoader,自定义
双亲委派机制加载类
- 检查是否已加载
- 如果未加载则上级类加载器
- 上级类加载器检查是否已加载。如果是 Bootstrap ClassLoader,如果未加载则检查类加载器的加载目录是否存在指定类
- 如果上级加载器也没有找到,则检查自身的类加载目录是否有该类
- 有则返回,没有就抛异常
实现自定义类加载器
- 继承ClassLoader父类
- 要遵从双亲委派机制,重写findClass方法
- 不是重写loadClass方法,否则不会走双亲委派机制
- 读取类的字节码文件
- 调用父类的defineClass方法来加载类
- 使用者调用该类加载器的loadClass方法
类相同的条件
- 类加载器相同
- 包名相同
- 类名相同
运行期优化
分层编译
- 解释器,将字节码编译为通用的机器码
- 解释器即使下次遇到相同的机器码仍然会重复执行
- JIT,将字节码编译为平台特定的机器码
- JIT会将翻译的机器码存入Code Cache,下次遇到相同的字节码,直接执行,无需编译