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

作者: 迦叶_金色的人生_荣耀而又辉煌 | 来源:发表于2020-12-18 08:06 被阅读0次

    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."<init>":()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        <init>;
    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;//  "<init>":()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."<init>":()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文件字节码结构组织示意图

    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 在线转换器进制转换工具

    相关文章

      网友评论

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

        本文链接:https://www.haomeiwen.com/subject/iyawgktx.html