Class文件分析一个类为啥最多支持65535个接口

上一篇 << 下一篇 >>>如何自定义注解


java源代码

public class UserEntity {

    private String userName;

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }
}

javac编译后字节码文件内容

CA FE BA BE 00 00 00 34 00 18 0A 00 04 00 14 09
00 03 00 15 07 00 16 07 00 17 01 00 08 75 73 65
72 4E 61 6D 65 01 00 12 4C 6A 61 76 61 2F 6C 61
6E 67 2F 53 74 72 69 6E 67 3B 01 00 06 3C 69 6E
69 74 3E 01 00 03 28 29 56 01 00 04 43 6F 64 65
01 00 0F 4C 69 6E 65 4E 75 6D 62 65 72 54 61 62
6C 65 01 00 12 4C 6F 63 61 6C 56 61 72 69 61 62
6C 65 54 61 62 6C 65 01 00 04 74 68 69 73 01 00
20 4C 63 6F 6D 2F 6A 61 72 79 65 2F 6A 61 76 61
61 73 69 73 74 2F 55 73 65 72 45 6E 74 69 74 79
3B 01 00 0B 67 65 74 55 73 65 72 4E 61 6D 65 01
00 14 28 29 4C 6A 61 76 61 2F 6C 61 6E 67 2F 53
74 72 69 6E 67 3B 01 00 0B 73 65 74 55 73 65 72
4E 61 6D 65 01 00 15 28 4C 6A 61 76 61 2F 6C 61
6E 67 2F 53 74 72 69 6E 67 3B 29 56 01 00 0A 53
6F 75 72 63 65 46 69 6C 65 01 00 0F 55 73 65 72
45 6E 74 69 74 79 2E 6A 61 76 61 0C 00 07 00 08
0C 00 05 00 06 01 00 1E 63 6F 6D 2F 6A 61 72 79
65 2F 6A 61 76 61 61 73 69 73 74 2F 55 73 65 72
45 6E 74 69 74 79 01 00 10 6A 61 76 61 2F 6C 61
6E 67 2F 4F 62 6A 65 63 74 00 21 00 03 00 04 00
00 00 01 00 02 00 05 00 06 00 00 00 03 00 01 00
07 00 08 00 01 00 09 00 00 00 2F 00 01 00 01 00
00 00 05 2A B7 00 01 B1 00 00 00 02 00 0A 00 00
00 06 00 01 00 00 00 03 00 0B 00 00 00 0C 00 01
00 00 00 05 00 0C 00 0D 00 00 00 01 00 0E 00 0F
00 01 00 09 00 00 00 2F 00 01 00 01 00 00 00 05
2A B4 00 02 B0 00 00 00 02 00 0A 00 00 00 06 00
01 00 00 00 08 00 0B 00 00 00 0C 00 01 00 00 00
05 00 0C 00 0D 00 00 00 01 00 10 00 11 00 01 00
09 00 00 00 3E 00 02 00 02 00 00 00 06 2A 2B B5
00 02 B1 00 00 00 02 00 0A 00 00 00 0A 00 02 00
00 00 0C 00 05 00 0D 00 0B 00 00 00 16 00 02 00
00 00 06 00 0C 00 0D 00 00 00 00 00 06 00 05 00
06 00 01 00 01 00 12 00 00 00 02 00 13         

javap -c -v Class文件反编译后效果

  minor version: 0
  major version: 52
  Constant pool:
const #1 = Method       #4.#20; //  java/lang/Object."":()V
const #2 = Field        #3.#21; //  com/jarye/javaasist/UserEntity.userName:Ljava/lang/String;
const #3 = class        #22;    //  com/jarye/javaasist/UserEntity
const #4 = class        #23;    //  java/lang/Object
const #5 = Asciz        userName;
const #6 = Asciz        Ljava/lang/String;;
const #7 = Asciz        ;
const #8 = Asciz        ()V;
const #9 = Asciz        Code;
const #10 = Asciz       LineNumberTable;
const #11 = Asciz       LocalVariableTable;
const #12 = Asciz       this;
const #13 = Asciz       Lcom/jarye/javaasist/UserEntity;;
const #14 = Asciz       getUserName;
const #15 = Asciz       ()Ljava/lang/String;;
const #16 = Asciz       setUserName;
const #17 = Asciz       (Ljava/lang/String;)V;
const #18 = Asciz       SourceFile;
const #19 = Asciz       UserEntity.java;
const #20 = NameAndType #7:#8;//  "":()V
const #21 = NameAndType #5:#6;//  userName:Ljava/lang/String;
const #22 = Asciz       com/jarye/javaasist/UserEntity;
const #23 = Asciz       java/lang/Object;

{
public com.jarye.javaasist.UserEntity();
  Code:
   Stack=1, Locals=1, Args_size=1
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."":()V
   4:   return
  LineNumberTable: 
   line 3: 0

  LocalVariableTable: 
   Start  Length  Slot  Name   Signature
   0      5      0    this       Lcom/jarye/javaasist/UserEntity;


public java.lang.String getUserName();
  Code:
   Stack=1, Locals=1, Args_size=1
   0:   aload_0
   1:   getfield        #2; //Field userName:Ljava/lang/String;
   4:   areturn
  LineNumberTable: 
   line 8: 0

  LocalVariableTable: 
   Start  Length  Slot  Name   Signature
   0      5      0    this       Lcom/jarye/javaasist/UserEntity;


public void setUserName(java.lang.String);
  Code:
   Stack=2, Locals=2, Args_size=2
   0:   aload_0
   1:   aload_1
   2:   putfield        #2; //Field userName:Ljava/lang/String;
   5:   return
  LineNumberTable: 
   line 12: 0
   line 13: 5

  LocalVariableTable: 
   Start  Length  Slot  Name   Signature
   0      6      0    this       Lcom/jarye/javaasist/UserEntity;
   0      6      1    userName       Ljava/lang/String;


}

使用jclasslib插件查看效果

打开idea 中的settings > plugins 搜索 jclasslib 插件 进行安装 重启生效
重启后点击view > 选择show bytecode with jclasslib

Class文件分析一个类为啥最多支持65535个接口_第1张图片

Class文件字节码结构组织示意图

Class文件分析一个类为啥最多支持65535个接口_第2张图片

Class文件整体结构说明

ClassFile { 
    u4 magic; //java的Class文件的标志,头四个字节固定为"CA FE BA BE"
    u2 minor_version;//Class 的小版本号 "00 00"
    u2 major_version;//Class 的大版本号 "00 34"转为十进制为52,即jdk8版本
    u2 constant_pool_count;//常量池的数量 "00 18"十进制为24,参考javap反编译中的(const #23,其中0位虚拟机默认)
    cp_info constant_pool[constant_pool_count-1];//常量池 
    u2 access_flags;//Class 的访问标记 
    u2 this_class;//当前类 
    u2 super_class;//父类 
    u2 interfaces_count;//接口 
    u2 interfaces[interfaces_count];//一个类可以实现多个接口 
    u2 fields_count;//Class 文件的字段属性 
    field_info fields[fields_count];//一个类会可以有个字段 
    u2 methods_count;//Class 文件的方法数量 
    method_info methods[methods_count];//一个类可以有个多个方法 
    u2 attributes_count;//此类的属性表中的属性数 
    attribute_info attributes[attributes_count];//属性表集合 
}
tips:u后面代码字节码位数,字节码文件中相邻两位"0A"代表1个字节码

常量池中的字面量与符号引用

类 型 标 志 描 述
CONSTANT_Utf8_info 1(后2个字节是长度) UTF-8编码的字符串
CONSTANT_Integer_info 3 整型字面量
CONSTANT_Float_info 4 浮点型字面量
CONSTANT_Long_info 5 长整形字面量
CONSTANT_Double_info 6 双精度浮点型字面量
CONSTANT_Class_info 7(3个字节) 类或接口的符号引用
CONSTANT_String_info 8 字符串类型字面量
CONSTANT_Fieldref_info 9(5个字节) 字段的符号引用
CONSTANT_Methodref_info 10(5个字节) 方法的符号引用
CONSTANT_InterfaceMethodref_info 11 接口的方法符号引用
CONSTANT_NameAndType_info 12 字段或方法的部分符号引用
CONSTANT_MethodHandle_info 15 表示方法句柄
CONSTANT_MethodType_info 16 标识方法类型
CONSTANT_InvokeDynamic_info 18 表示一个动态方法调用点

类或接口的访问标志

名称 备注
ACC_PUBLIC 0x0001 表示访问权限为public,可以从本包外访问
ACC_FINAL 0x0010 表示由final修饰,不允许有子类
ACC_SUPER 0x0020 较为特殊,表示动态绑定直接父类,见下面的解释
ACC_INTERFACE 0x0200 表示接口,非类
ACC_ABSTRACT 0x0400 表示抽象类,不能实例化
ACC_SYNTHETIC 0x1000 表示由synthetic修饰,不在源代码中出现
ACC_ANNOTATION 0x2000 表示是annotation类型
ACC_ENUM 0x4000 表示是枚举类型
ACC_PRIVATE 0X0002 表示私有

字段属性表结构

field_info {
    u2 access_flags;// 字段的作用域( public , private , protected 修饰符),是实例变量还是类变量( static 修饰符),可否被序列化(transient 修饰符),可变性(final),可见性(volatile 修饰符,是否强制从主内存读写)。
    u2 name_index; // 对常量池的引用,表示的字段的名称;
    u2 descriptor_index;// 对常量池的引用,表示字段和方法的描述符;
    u2 attributes_count;// 一个字段还会拥有一些额外的属性,attributes_count 存放属性的个数;
    attribute_info attributes[attributes_count]; //具体的属性内容
}

字段属性access_flags 表

名称 备注
ACC_PUBLIC 0x0001 public,方法可以从包外访问
ACC_PRIVATE 0x0002 private,方法只能本类中访问
ACC_PROTECTED 0x0004 protected,方法在自身和子类可以访问
ACC_STATIC 0x0008 static,静态方法
ACC_FINAL 0x0010 final,方法不能被重写(覆盖)
ACC_VOLATILE 0x0040 volatile可见性
ACC_TRANSIENT 0x0080 序列化
ACC_SYNTHETIC 0x1000 方法在源文件中不出现,由编译器产生
ACC_ENUM 0x4000 枚举类型

方法表结构

method_info {
    u2 access_flags;
    u2 name_index; 
    u2 descriptor_index;
    u2 attributes_count;
    attribute_info attributes[attributes_count]; 
}

方法access_flags 表

名称 备注
ACC_PUBLIC 0x0001 public,方法可以从包外访问
ACC_PRIVATE 0x0002 private,方法只能本类中访问
ACC_PROTECTED 0x0004 protected,方法在自身和子类可以访问
ACC_STATIC 0x0008 static,静态方法
ACC_FINAL 0x0010 final,方法不能被重写(覆盖)
ACC_SYNCHRONIZED 0x0020 synchronized,方法由管程同步
ACC_BRIDGE 0x0040 bridge,方法由编译器产生
ACC_VARARGS 0x0080 表示方法带有变长参数
ACC_NATIVE 0x0100 native,方法引用非 java 语言的本地方法
ACC_ABSTRACT 0x0400 abstract,方法没有具体实现
ACC_STRICT 0x0800 strictfp,方法使用 FP-strict 浮点格式
ACC_SYNTHETIC 0x1000 方法在源文件中不出现,由编译器产生

属性结构

Code_attribute {
    u2 attribute_name_index(2); //常量池中的uft8类型的索引,值固定为”Code“
    u4 attribute_length(4); //属性值长度,为整个属性表长度-6
    u2 max_stack;   //操作数栈的最大深度值,jvm运行时根据该值分配栈帧
    u2 max_locals;  //局部变量表最大存储空间,单位是slot
    u4 code_length; // 字节码指令的个数
    u1 code[code_length]; // 具体的字节码指令
    u2 exception_table_length; //异常的个数
    {   u2 start_pc;
        u2 end_pc;
        u2 handler_pc; //当字节码在[start_pc, end_pc)区间出现catch_type或子类,则转到handler_pc行继续处理。
        u2 catch_type; //当catch_type=0,则任意异常都需转到handler_pc处理
    } exception_table[exception_table_length]; //具体的异常内容
    u2 attributes_count;     //属性的个数
    attribute_info attributes[attributes_count]; //具体的属性内容
}

其中slot为局部变量中的最小单位。boolean、 byte、 char、 short、 float、 reference和 returnAddress 等小于等于32位的用一个slot表示,double,long这些大于32位的用2个slot表示。

为什么class文件需要通过2进制排列?

目的就是为了能够压缩,减少类加载到元空间存储的大小,避免fullGC的问题。

为什么一个类最多只能实现65535个接口?

一个类的最大接口数是有2个字节组成,使用16进制最多只支持FFFF,十进制为65535个接口。
u2 interfaces[interfaces_count];//一个类可以实现多个接口

项目中使用工具:十六进制和ASCII 在线转换器、进制转换工具


相关文章链接:
<< << << << << <<<为什么重写equals还要重写hashcode方法
<<<如何自定义注解
<<<十大经典排序算法汇总-动画演示
<<

你可能感兴趣的:(Class文件分析一个类为啥最多支持65535个接口)