计算器只认识0和1,那么我们写的java代码是怎么被操作系统识别并执行的呢?
其实这里就是虚拟机起了很多作用了,JVM定义了一套字节码规范,只要符合虚拟机规范的字节码就可以被虚拟机识别,加载,执行 ,class文件就是字节码的来源,JVM通过 ,可见CLASS文件是JVM平台的基石。
class文件结构是一组以8位字节为基础单位的二进制流,各个数据项目严格按照顺序紧凑的排列在class文件之中,中间没有任何分隔符。
包含两种数据结构:
1、无符号数:基本的数据类型,以u1,u2,u4,u8代表1,2,4,8个字节的无符号数,用来描述数字,索引引用,数量值
2、表:由无符号数或者其他表作为数据项构成的复合数据类型,以_info结尾
u4 magic 魔术, 身份识别,是否可以被虚拟机接受
u2 minor_version 高版本可以兼任低版本 jdk1 以45为版本号
u2 major_version
cp_info 常量池 存储字面量和符合引用 类和接口全限定名 字段的名称和描述符 方法的名称和描述符
javap a.class javap命令可以查询class文件字节码
类加载机制
class文件格式了解了,但是这些class文件是怎么被虚拟机识别加载到内存的,这里会经历一些过程。
类加载生命周期:加载,验证,准备,解析,初始化,使用,卸载
加载:通过类全限定名进行获取二进制字节流
验证:文件格式,元数据,字节码,符号引用等验证
准备:为类变量分配类型并设置类初始值
解析:将符号引用替换成直接引用,包括类或接口的解析,字段解析,类方法解析,接口方法解析
初始化:当遇到new ,getstatic,puttstatic,invokestatic指令时进行初始化,方法中会对变量赋值或者静态代码块的初始化
静态代码块只能访问在静态代码块定义之前的变量,定义在它之后的变量可以赋值,但是不能访问,并且总是会先执行父类的方法
虚拟机天然保证在多线程下会正确执行,只会有一个线程同时执行
类加载机制:双亲委派模型
启动类加载器:加载java_home/lib下的jar文件
扩展加载器:加载java_home/lib/ext的jar文件
应用类加载器:加载用户类路径classpath下的类文件
双亲委派模型以组合关系来复用父类加载器代码。
工作过程:一个类加载器收到加载类请求时,首先会把加载请求委派父类加载器去完成,只有当父类加载器反馈自己加载不了当前请求的类,它才会自己去加载。如下图:
双亲委派机制好处:保证一个类只会被一个类加载器加载,保证了系统的稳定可靠性。使得java类有了一种层级关系。
破坏双亲委派机制的例子:
spi提供的接口的实现类启动器类无法加载,所以有了线程上下文类加载器,例如jdbc,jndi