备案 控制台
开发者社区 开发与运维 文章 正文

Java Record 的一些思考 - 默认方法使用以及基于预编译生成相关字节码的底层实现(上)

简介: Java Record 的一些思考 - 默认方法使用以及基于预编译生成相关字节码的底层实现(上)

快速上手 Record 类


我们先举一个简单例子,声明一个用户 Record。

public record User(long id, String name, int age) {}

这样编写代码之后,Record 类默认包含的元素和方法实现包括:

  1. record 头指定的组成元素(int id, String name, int age),并且,这些元素都是 final 的。
  2. record 默认只有一个构造器,是包含所有元素的构造器。
  3. record 的每个元素都有一个对应的 getter(但这种 getter 并不是 getxxx(),而是直接用变量名命名,所以使用序列化框架,DAO 框架都要注意这一点)
  4. 实现好的 hashCode(),equals(),toString() 方法(通过自动在编译阶段生成关于 hashCode(),equals(),toString() 方法实现的字节码实现)。

我们来使用下这个 Record :

User zhx = new User(1, "zhx", 29);
User ttj = new User(2, "ttj", 25);
System.out.println(zhx.id());//1
System.out.println(zhx.name());//zhx
System.out.println(zhx.age());//29
System.out.println(zhx.equals(ttj));//false
System.out.println(zhx.toString());//User[id=1, name=zhx, age=29]
System.out.println(zhx.hashCode());//3739156


Record 的结构是如何实现的


编译后插入相关域与方法的字节码

查看上面举得例子的字节码,有两种方式,一是通过 javap -v User.class 命令查看文字版的字节码,截取重要的字节码如下所示:

//省略文件头,文件常量池部分
{
  //public 构造器,全部属性作为参数,并给每个 Field 赋值
  public com.github.hashzhang.basetest.User(long, java.lang.String, int);
    descriptor: (JLjava/lang/String;I)V
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=3, locals=5, args_size=4
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Record."<init>":()V
         4: aload_0
         5: lload_1
         6: putfield      #7                  // Field id:J
         9: aload_0
        10: aload_3
        11: putfield      #13                 // Field name:Ljava/lang/String;
        14: aload_0
        15: iload         4
        17: putfield      #17                 // Field age:I
        20: return
      LineNumberTable:
        line 3: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      21     0  this   Lcom/github/hashzhang/basetest/User;
            0      21     1    id   J
            0      21     3  name   Ljava/lang/String;
            0      21     4   age   I
    MethodParameters:
      Name                           Flags
      id
      name
      age
  //public final 修饰的 toString 方法
  public final java.lang.String toString();
    descriptor: ()Ljava/lang/String;
    flags: (0x0011) ACC_PUBLIC, ACC_FINAL
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         //核心实现是这个 invokedynamic,我们后面会分析
         1: invokedynamic #21,  0             // InvokeDynamic #0:toString:(Lcom/github/hashzhang/basetest/User;)Ljava/lang/String;
         6: areturn
      LineNumberTable:
        line 3: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       7     0  this   Lcom/github/hashzhang/basetest/User;
  //public final 修饰的 hashCode 方法
  public final int hashCode();
    descriptor: ()I
    flags: (0x0011) ACC_PUBLIC, ACC_FINAL
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         //核心实现是这个 invokedynamic,我们后面会分析
         1: invokedynamic #25,  0             // InvokeDynamic #0:hashCode:(Lcom/github/hashzhang/basetest/User;)I
         6: ireturn
      LineNumberTable:
        line 3: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       7     0  this   Lcom/github/hashzhang/basetest/User;
  //public final 修饰的 equals 方法
  public final boolean equals(java.lang.Object);
    descriptor: (Ljava/lang/Object;)Z
    flags: (0x0011) ACC_PUBLIC, ACC_FINAL
    Code:
      stack=2, locals=2, args_size=2
         0: aload_0
         1: aload_1
         //核心实现是这个 invokedynamic,我们后面会分析
         2: invokedynamic #29,  0             // InvokeDynamic #0:equals:(Lcom/github/hashzhang/basetest/User;Ljava/lang/Object;)Z
         7: ireturn
      LineNumberTable:
        line 3: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       8     0  this   Lcom/github/hashzhang/basetest/User;
            0       8     1     o   Ljava/lang/Object;
  //public 修饰的 id 的 getter
  public long id();
    descriptor: ()J
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=2, locals=1, args_size=1
         0: aload_0
         1: getfield      #7                  // Field id:J
         4: lreturn
      LineNumberTable:
        line 3: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcom/github/hashzhang/basetest/User;
  //public 修饰的 name 的 getter
  public java.lang.String name();
    descriptor: ()Ljava/lang/String;
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: getfield      #13                 // Field name:Ljava/lang/String;
         4: areturn
      LineNumberTable:
        line 3: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcom/github/hashzhang/basetest/User;
  //public 修饰的 age 的 getter
  public int age();
    descriptor: ()I
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: getfield      #17                 // Field age:I
         4: ireturn
      LineNumberTable:
        line 3: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcom/github/hashzhang/basetest/User;
}
SourceFile: "User.java"
Record:
  long id;
    descriptor: J
  java.lang.String name;
    descriptor: Ljava/lang/String;
  int age;
    descriptor: I
//以下是 invokedynamic 会调用的方法以及参数信息,我们后面会分析
BootstrapMethods:
  0: #50 REF_invokeStatic java/lang/runtime/ObjectMethods.bootstrap:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/TypeDescriptor;Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/invoke/MethodHandle;)Ljava
/lang/Object;
    Method arguments:
      #8 com/github/hashzhang/basetest/User
      #57 id;name;age
      #59 REF_getField com/github/hashzhang/basetest/User.id:J
      #60 REF_getField com/github/hashzhang/basetest/User.name:Ljava/lang/String;
      #61 REF_getField com/github/hashzhang/basetest/User.age:I
InnerClasses:
  public static final #67= #63 of #65;    // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles

另一种是通过 IDE 的 jclasslib 插件查看,我推荐使用这种方法,查看效果如下:



image.png

干货满满张哈希
目录
相关文章
长梦
|
3天前
|
安全 Java 编译器
Java一分钟之——泛型方法与泛型接口
【5月更文挑战第20天】Java泛型提供编译时类型安全检查,提升代码重用和灵活性。本文探讨泛型方法和接口的核心概念、常见问题和避免策略。泛型方法允许处理多种数据类型,而泛型接口需在实现时指定具体类型。注意类型擦除、误用原始类型和泛型边界的理解。通过明确指定类型参数、利用通配符和理解类型擦除来避免问题。泛型接口要精确指定类型参数,适度约束,利用默认方法。示例代码展示了泛型方法和接口的使用。
长梦
29 1
Java一分钟之——泛型方法与泛型接口
秋说
|
8天前
|
Java 编译器
【Java开发指南 | 第十九篇】Java方法
【Java开发指南 | 第十九篇】Java方法
秋说
10 0
游客762btuqu5wybw666
|
3天前
|
算法 Java 编译器
从Java字节码到JIT编译器,深入理解Java虚拟机
Java虚拟机(JVM)是Java程序运行的关键。想深入理解Java虚拟机,我们需要了解Java字节码、类加载机制、垃圾回收算法、JIT编译器等方面的知识。本文将介绍这些关键知识点,并通过示例代码加深理解。
游客762btuqu5wybw666
9 0
bug菌
|
3天前
|
Java
滚雪球学Java(35):揭秘Java方法的返回值,从void到诸多数据类型
【5月更文挑战第10天】🏆本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
bug菌
12 0
滚雪球学Java(35):揭秘Java方法的返回值,从void到诸多数据类型
喜欢猪猪
|
3天前
|
并行计算 Java API
Java 8中的接口默认方法和静态方法以及并行数组
【5月更文挑战第19天】Java 8引入了许多新特性,其中包括接口的默认方法和静态方法,以及并行数组的能力。这些特性增强了Java的面向对象编程模型和数组处理能力。让我们深入了解它们的概念和实践。
喜欢猪猪
21 2
bug菌
|
4天前
|
Java 编译器
滚雪球学Java(34):探究Java方法的神奇魔法和参数传递奥秘
【5月更文挑战第9天】🏆本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
bug菌
11 1
滚雪球学Java(34):探究Java方法的神奇魔法和参数传递奥秘
老板这功能得加钱
|
5天前
|
Java
Java中int[]与Integer[]相互转化的方法,java基础知识面试重点总结
Java中int[]与Integer[]相互转化的方法,java基础知识面试重点总结
老板这功能得加钱
10 1
xiaowang_lj
|
7天前
|
Java
Java String 避免空指针的方法
Java String 避免空指针的方法
xiaowang_lj
7 0
秋说
|
8天前
|
Java 编译器
【Java开发指南 | 第十七篇】Java 方法
【Java开发指南 | 第十七篇】Java 方法
秋说
8 1
秋说
|
8天前
|
Java
【Java开发指南 | 第九篇】访问实例变量和方法、继承、接口
【Java开发指南 | 第九篇】访问实例变量和方法、继承、接口
秋说
15 4

热门文章

最新文章

  • 1
    使用Redis进行Java缓存策略设计
  • 2
    Java语言开发的AI智慧导诊系统源码springboot+redis 3D互联网智导诊系统源码
  • 3
    【JAVA】Java 中什么叫单例设计模式?请用 Java 写出线程安全的单例模式
  • 4
    基于Java的公司员工工作日志办公系统的设计与实现(源码+lw+部署文档+讲解等)
  • 5
    【JAVA】线程的run()和start()有什么区别?
  • 6
    高德地图爬虫实践:Java多线程并发处理策略
  • 7
    [AIGC] 21世纪Java与Go的相爱相杀
  • 8
    Java多线程实战-CompletableFuture异步编程优化查询接口响应速度
  • 9
    打造个人的Minecraft服务器:Java+cpolar实现我的世界联机游戏
  • 10
    Java 开发者必备:JDK 版本详解与选择策略(含安装与验证)
  • 1
    Java中的集合框架:深入理解与应用
    48
  • 2
    Java并发编程:理解并使用Future和Callable接口
    24
  • 3
    性能工具之 Java 调试工具 JDB
    51
  • 4
    JAVA云HIS系统医院信息管理源码
    37
  • 5
    Java基于物联网技术的智慧工地云管理平台源码 依托丰富的设备接口标准库,快速接入工地现场各类型设备
    157
  • 6
    [Java基础]——类的生命周期
    20
  • 7
    [java进阶]——泛型类、泛型方法、泛型接口、泛型的通配符
    37
  • 8
    【java进阶】Java中线程的实现方式
    27
  • 9
    【java进阶】集合的三种遍历(迭代器、增强for、Lambda)
    22
  • 10
    [java进阶]——方法引用改写Lambda表达式
    37
  • 相关课程

    更多
  • Java面试疑难点解析 - 面试技巧及语言基础
  • Java面试疑难点解析 - Java Web开发
  • Java面试疑难点解析 - 系统架构及项目设计
  • Java编程入门
  • Java面向对象编程
  • Java高级编程
  • 相关电子书

    更多
  • Spring Cloud Alibaba - 重新定义 Java Cloud-Native
  • The Reactive Cloud Native Arch
  • JAVA开发手册1.5.0
  • 相关实验场景

    更多
  • 对象和接口-2:常见用法
  • 阿里云平台上进行Java程序的编译与运行
  • 使用Java面向对象编写网络通信程序应用
  • Elasticsearch Java API Client 开发
  • 手动部署Java Web环境(Alibaba Cloud Linux 2)
  • 搭建Java Web开发环境(Anolis OS)
  • 下一篇
    2024年阿里云免费云服务器及学生云服务器申请教程参考

    两个鬼故事狗不理包子姓土怎么起名楚河汉街地址新唐书男男生行为网站宝宝起名软件 免费游戏起个啥名国贸公司起名大全门业起名字秀华朱大海全文免费阅读课程起名字欧洲冠军杯历届冠军生源地是什么意思?佛山游资cctv5超清田忌赛马赛的故事正能量的话励志的语句开店免费起名网取名属鼠孩子起名字禁忌序列号英文揭阳交警公司起名重名我的傻白甜老婆最新章节格美起名网免费测名打分起名子女孩千易答鼠年起名字中国vs阿联酋丁俊晖排名赛尔号6苍穹烈火特种部队3少年生前被连续抽血16次?多部门介入两大学生合买彩票中奖一人不认账让美丽中国“从细节出发”淀粉肠小王子日销售额涨超10倍高中生被打伤下体休学 邯郸通报单亲妈妈陷入热恋 14岁儿子报警何赛飞追着代拍打雅江山火三名扑火人员牺牲系谣言张家界的山上“长”满了韩国人?男孩8年未见母亲被告知被遗忘中国拥有亿元资产的家庭达13.3万户19岁小伙救下5人后溺亡 多方发声315晚会后胖东来又人满为患了张立群任西安交通大学校长“重生之我在北大当嫡校长”男子被猫抓伤后确诊“猫抓病”测试车高速逃费 小米:已补缴周杰伦一审败诉网易网友洛杉矶偶遇贾玲今日春分倪萍分享减重40斤方法七年后宇文玥被薅头发捞上岸许家印被限制高消费萧美琴窜访捷克 外交部回应联合利华开始重组专访95后高颜值猪保姆胖东来员工每周单休无小长假男子被流浪猫绊倒 投喂者赔24万小米汽车超级工厂正式揭幕黑马情侣提车了西双版纳热带植物园回应蜉蝣大爆发当地回应沈阳致3死车祸车主疑毒驾恒大被罚41.75亿到底怎么缴妈妈回应孩子在校撞护栏坠楼外国人感慨凌晨的中国很安全杨倩无缘巴黎奥运校方回应护栏损坏小学生课间坠楼房客欠租失踪 房东直发愁专家建议不必谈骨泥色变王树国卸任西安交大校长 师生送别手机成瘾是影响睡眠质量重要因素国产伟哥去年销售近13亿阿根廷将发行1万与2万面值的纸币兔狲“狲大娘”因病死亡遭遇山火的松茸之乡“开封王婆”爆火:促成四五十对奥巴马现身唐宁街 黑色着装引猜测考生莫言也上北大硕士复试名单了德国打算提及普京时仅用姓名天水麻辣烫把捣辣椒大爷累坏了

    两个鬼故事 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化