类文件结构描述

java语言平台无关性

  • java语言为什么平台无关:虚拟机运行在不同的平台上,但是这些虚拟机都可以加载和执行字节码文件,这样字节码文件就可以在不同的平台上执行。(即:虚拟机是与平台相关的,但是字节码文件是与与平台无关)。

Class类文件结构(构成)

class文件特点:
(1)一组以8个字节为基础单位的二进制流,各个数据项目紧凑排列在文件之中,没有任何分隔符。
(2)文件中只有两种数据类型:无符号数和表
无符号数:属于基本的数据类型,u1,u2,u4,u8分别表示1、2、4、8字节的无符号数。无符号数用来描述:数字、索引引用、数量值、或者按照utf-8编码的字符串值。
:由多个无符号数或者其他表作为数据项构成的符合数据类型,表的命名格式“_info”结尾。

  • 1魔术:oxFA FE BA BE,4字节
    (1)定义:Class文件前4个字节称为魔术(oxCAFEBABE),
    (2)作用:标识该文件是一个Class文件,可以被虚拟机接受。之所以不用扩展名来标识,而放在文件里面来标识,因为扩展名容易被修改。
  • == 2版本号==:
    (1)定义:4字节长。前两字节(即class文件的第5,6字节):次版本号,后两字节:主版本号码
    (2)主版本号码从45开始。即当主版本号码为45时候,标识该class文件可以被jdk1即以上的虚拟机执行。当主版本号为50,50-45+1=6,表明该class文件能够本jdk6及以上版本(向上兼容)的jvm执行。
  • == 3常量池==:
    虚拟机加载class文件的时候进行动态连接。即,在class文件中不保存各个方法、字段最终在内存中的布局信息,这些字段、方法的符号引用不经过虚拟机在运行期转换的话是无法得到真正的内存入口地址,也就无法直接被虚拟机使用的。当虚拟机做类加载时,将会从常量池中获得对应的符号引用,再在类创建时或运行时解析、翻译到具体的内存地址中。
    class文件中很多数据项都要引用常量池中的常量

    (1)常量池的入口:常量池容量计数值(2字节长),表示常量池中常量的个数,从1开始。class文件结构中只有常量池的容量计数从1开始。
    (2)常量池的构成:
    **字面量:****符号引用:**
    其中字面量就是在编译期间就能确定的变量的值,如String str = “str”,final修饰的变量(final修饰的变量不会发生变化)。符号引用那个是类和字段完全限定名。
    (3)常量池中的组织结构
    a、截止jdk13,常量池中有17种常量。(最初有11种常量,jdk新增了3种,jdk新增了1中,之后这四种为了更好支持动态语言调用)
    b、17种常量,对应17个表结构,表结构的起始第一位是标志位(tag,1u类型)表示当前常量属于哪种类型。17中常量见下表:

在这里插入图片描述
c、每个常量都对应一个表,举例如下:
下图是类型为Constant_class_info的常量结构,表示一个类或者一个接口的符号引用。该常量包括两个数据项,标志位(1u,用于区分常量类型,与常量表中对应)和name_index(2字节无符号数)是class常量池中的索引值。即指向常量池中的另外一个常量。
在这里插入图片描述
下面是类型为constant_utf8_info的常量结构,tag是一个u1(占1字节的无符号数),表示对应的常量类型,length表示UTF8缩略编码的字符串长度,bytes表示长度为length字节的utf-8缩略编码的字符串。
在这里插入图片描述
Class文件中方法与字段都需要用constant_utf8_info来描述名称,所以constant_utf8_info类型常量的最大长度也就是方法、字段名的最大长度。在该常量中,length为u2类型,占16位,即方法与字段名不能超过2^16=64KB

  • 4访问标志符
    (1)由2个字节表示,即16位表示,每1位代表一个访问标志,若该标志为真,则为1,否则为0.
    (2)访问标志符用于标识类或者接口的访问信息。如:是类还是接口、是否为abstract类、是否为public修饰、如果是类的话,是否声明为final等
    目前定义的访问标志如下,目前之定义了9个标志为:
    在这里插入图片描述
    jdk1.0.2之后,acc_super默认为真,如果一个类只声明为public,那么这个这个类的访问标志符为:ox0001 | ox0020 = ox0021
  • 5类索引、父类索引、接口索引集合
    (1)格式:类索引(this_class)和父类索引(super_class)是u2类型数据,接口结合(interfaces)是一组u2类型的数据的集合。
    (2)作用:类索引、父类索引、接口索引集合这三项数据可以确定一个类的继承关系(继承的父类以及多个接口)。
    (3)类索引和父类索引都是指向常量池中的constant_class_info类型常量。下图描述了一个根据类索引查找到类的全限定名字符串的过程(类索引值为1,即常量池中索引为1的常量(为一个constant_class_info类型常量),该类型常量中定位了一个constant_utf8_info常量,这个utf8常量中存在这一个utf-8编码的字符串,即为类的全限定名。
    在这里插入图片描述
    (4)对于接口索引集合,入口是接口计数器(u2类型),表示接口索引的容量,紧接着计数器的后面,就是n个(计数器个)u2类型的索引值,每个索引指向常量池中的一个常量,和类索引一样。
  • 6字段表集合
    几个名词的区分
    (1)类的全限定名:即类的路径+名字,且“.”用“/”表示,如类java/lang/String;以分号结尾
    (2)简单名称:即方法名称或者是字段的名称。如方法public int inc()方法的简单名称为inc。字段int m的简单名称为m。
    (3)(方法和字段)描述符
    描述符作用:描述字段的类型、或者是方法的参数列表(包括:类型、数量、顺序)和返回值。
    描述规则:
    (1)对字段的描述:
    基本类型(byte、boolean、char、short、int、float、long、double)以及void类型用一个大写字母表示;引用类型用L+类的全限定名(如字段类型为String,则字段描述符为:Ljava/lang/String;);数组类型:“[”(几维数组就有几个“[”)+基本类型描述|引用类型的描述(如数组int[] i则被记录为 “[I” )。
    在这里插入图片描述
    (2)对方法的描述:
    按照先参数列表,后返回值的顺序描述。参数按照顺序放到()中。如方法void inc()表示为:()V。如方法int indexOf(char[] source, int sourceOffset, int source)可以描述为([CII)I。
    一个字段应该包含的信息:(1)字段的修饰符(字段的作用域:public\private\protected;实例变量还是类变量:static;可变性:final;并发可见性:volatile;是否被序列化:transient)(2)字段的数据类型(基本变量、引用变量、数组)(3)字段名称(4)字段的额外信息(是否有初值)
    -------------------------------------------字段表集合正式讲解开始----------------
    字段表的作用:用于描述接口或者类中申明的变量(类变量以及实例变量)。
    格式:字段表集合第一数据项为u2类型的容量计数器,表示该类中有多少个字段。接下来就是n个字段表信息。字段表格式如下:
    在这里插入图片描述
    字段表中包含5部分信息:
    (1)访问修饰符号,占16位,,每一位表示一个修饰符,如果该字段被该修饰符修饰,则对应的位为1否则为0.
    在这里插入图片描述
    (2)name_index:表示常量池中对应的常量的索引。该索引位置上是constant_utf8_info常量类型,表示字段的简单名称。如字段public int m的简单名称为m。
    (3)descriptor_index:表示常量池中对应的常量的索引。常量池中该索引上表示的是字段的描述符即描述字段的类型信息。如public int m的描述符为:I;String m的描述符为:Ljava/lang/String;int[] m的描述符为:[I。
    (4)第4、5两个数据项表示属性表集合,用于存储字段的额外信息。attributes_count表示属性的数量,attributes表示n个属性。如当字段声明为:final static int m = 123;时,那么123这个值就需要额外的表示。
    字段表集合中不会列出父类或接口的字段,内部类中的字段表会添加外部类的字段(为了保持对外部类的访问性)。另外,字段无法重载,类中不能存在相同字段名的字段,但是对于class文件格式来讲,只要两个字段的描述符不同(即类型不同)就是合法的字段
  • 7方法表集合
    (1)方法表集合的在开始的数据项是一个u2类型的数据,表示该class文件中方法的个数,之后的就是n个方法表的信息。
    (2)class文件中,对方法和字段的描述采用集合一直的方式。方法表的格式如下:
    在这里插入图片描述
    访问标志表如下:
    在这里插入图片描述
    方法中的代码放在了属性表(Code属性表)中。
    与字段表一样,如果父类方法没有在子类中被重写,方法表集合中不会出现父类的方法信息。同样的,编译器可能会自动在方法表集合中添加方法,如类构造器“()”方法和实例构造器“()”方法
    java语言中重载一个方法,除了有相同的简单名称之外,还要与原方法有不同的特征签名(特征签名指方法中的各个参数在常量池中的字段符号引用的集合,即返回值不包含在特征签名中),所以java语言里无法仅仅根据返回值来对一个已有的方法重载。但是class文件中,特征签名包含方法的简单名称、方法的描述符(即把返回值包含在了方法的特征签名中),因此特征签名中包含了返回类型,这样两个方法拥有相同的名称和特征签名,但是返回值不同,就可以共存在class文件中
  • 8属性表集合
    (1)Code属性
    (a)Code属性中存放的内容:方法体中的代码别编译后生成的字节码指令。Code属性处在在方法表的属性集合中。接口或者抽象类的方法中不存在Code属性。Code属性表中各个数据项描述:
    在这里插入图片描述

(2)Exceptions属性
(3)LineNumberTable属性
(4)LocalVariableTable及LocalVariableTypeTable属性
(5)SourceFile及SourceDebugExtension属性
(6)ConstantValue属性
(7)InnerClasses属性
(8)Deprecated及Synthetic属性
(9)StackMapTable属性
(10)Signature属性
(11)BootstrapMethods属性
(12)MethodParameters属性
(13)模块化相关属性
(14)运行时注解相关属性

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值