目录
版本号minor_version/major_version
interfaces_count/interfaces[interfaces_count]
fields_count/fields[fields_count]
注2,静态块,实例块,构造方法的实行顺序先执行静态块再实例块接着是构造方法
1,Class文件理解
Class文件是什么
其实java虚拟机是不认识java语言的,他不可能直接通过java语言而运行的程序,他只与class文件这种特定的二进制文件格式所联系
除java外还有Kotlin,Clojure,Groovy,JRuby,JPython,Scala都可以编译出class文件的
class文件包含了java虚拟机的指令集,符号表,其他信息等
Class文件与类的关系
一个class文件对应唯一一个类或者借口的
一个类或者借口不一定只能是class文件因为还有动态代理,也就是类加载器直接生成出来的
Class文件的格式
class文件里面都是二进制,(字节流组成),每个字节都有8个二进制位所有16位32位64位长度的数据将通过构造成2个4个8个连续的8位字节来表示
查看class文件的工具:IDEA的Big-endian插件(大端小端的顺序进行存储)
一个普通的类文件中编译形成的class文件的数据显示:
大端存储就是低位在高地址中 高位数在低地址中 小端模式则相反:(图中是大端模式存储)
以图中的0034为例如果0034是一个十六进制数那么对他来说他的低位就是34高位是00
所以他将要34放到高地址中,就是07号地址,而00则是高位放在低地址中则是06号地址
2,Class文件结构
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];
}
class文件数据结构采用了两种数据类型:"无符号数"和"表"
无符号数:代表了class文件的内容如果是u1,u2,u4分别代表1,2,4个字节的无符号数
无符号数可以用来描述数字,引用,数值或者按照UTF-8编码构成字符串值
表:是由多个无符号数或者用其他表做数据构成的复合数据类型结尾习惯用_info命名
魔数magic;
class文件结构第一项:u4 magic
magic这里叫魔数占4个字节
魔数就是用四个字节来描述该文件类型的一个项 这四个字节叫做魔数
class文件魔数四个字节:CA FE BA BE也就是(大端)class文件开头的四个字节
魔数的利用:文本文件是没有魔数值的比如txt文件
利用魔数判断文件的真实类型
版本号minor_version/major_version
版本号就是class文件的就是文件的主版本号和次版本号
比如java8版本的版本号就是52 java11则是55 java1.1.8版本则是45.3
major_version主版本起始值为45每个JDK大版本发布会加1
minor_version次版本起始值为0
比如java1.1.8的主版本号就是45次版本号就是3
java8的主版本号52次版本0
注意:JVM是向下兼容而向上不兼容的 如jdk8可以运行jdk及8以下的class文件 则不能运行版本号超过52的class文件
class文件中的常量constant
假设常量池大小为n 常量池真正有效的索引为1到n-1
可以通过idea编译器的jclasslib插件来查看一个文件中的常量池
class文件中的常量:字面量,符号表
符号表类似下标
描述符就是描述数据类型,方法等的
常量池cp_info是一个常量数组每一个元素是一个常量
常量项中有一个tag项tag是标志一个常量是那种常量结构
根据常量不同jdk8中常量分为14种
access_flags
access_flags用来标识一个方法或者类等的访问权限属性
但在java8之后每个类会自动带一个父类就是说自动加一个ACC——SUPER 0x0020
this_class
this_class的值必须对表中CONSTANT_Class_info类型结构体,这个结构体表示这个class文件所定义的类或者接口
super_class
super_class和this_class差不多 用来标识父的名称
这个值要么为0要么是CONSTANT_Class_info
如果这个值为0那么这个class文件只可能用表示Object类
如果是一个接口如果有super_class那么这个super_class必须是Object类的CONSTANT_Class_info
interfaces_count/interfaces[interfaces_count]
是用来表示类或者接口的直接父接口
父接口的表示由接口总数和接口组成数组
每个父接口元素为CONTANT_Class_info
他用来表示父接口 数组用来表示多个接口
fields_count/fields[fields_count]
表示类中定义的字段(非静态和静态的字段)
Filed_info结构
access_flags表示访问权限属性如上面小标题的access_flags的差不多
name_index表示字段的名字
descriptor_index表示字段描述符的索引
attributes_count表示字段属性总数
attribute_info表示字段属性元素数组
适合字段属性分类有6种
class文件常量池中的fieldref_info类型是常量代表的是等号右边的字段的值
attribute_info属性分类:
1,ConstantValue:
表示静态变量的初始值static final修饰的 必须是值类型和string类型的(string类型不能new出来的)
第一个是名称 第二个长度 第三个具体的数值
2,Synthetic
类成员没有在源文件中出现,由编译器自动产生的
3,Signature
泛型签名
4,Deprecated
过时的字段
5,RuntimeVisibleAnnotations
使用的注解,并且运行时可见的注解(JVM可以反射读取)
注解定义是RetentionPolicy.RUNTIME的注解
6,RuntimeInvisibleAnnotations
使用的注解并且是运行时不可见注解(JVM不能反射读取到的)
注解定义是RetentionPolicy.CLASS的注解
CLASS是JVM不知道但是class文件知道的
注意点
注1,java接口中的基类是否为Object
任何接口是没有父类的 如果一个接口继承一个类那么会有三个常量池的CONSTANT_Class_info文件分别是:
他自己 Object 还有继承的类
我们假定在接口类中有一个Object obj = new Object();
那么在常量池中显示Object则正常 但是没有写Object
实际上在接口中会隐式的去实现Object的方法 但Object不是接口的基类 而是它的引用
注2,静态块,实例块,构造方法的实行顺序先执行静态块再实例块接着是构造方法
注3注解生命周期
RetentionPolicy.SOURCE——此注解类型的信息只会记录在源文件中,不会在class文件中
RetentionPolicy.CLASS——编译器将注解记录在class文件中,但无法加载到JVM中不会被反射读取
RetentionPolicy.RUNTIME——编译器将记录在class文件中并且加载到JVM,可以被反射读取到