Lombok 工具中@Data注解生成hashCode()可能导致StackOverflowError情况

本文主要是介绍Lombok 工具中@Data注解生成hashCode()可能导致StackOverflowError情况,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

 

看了大学一位同学博客,写了内容大致就是对于一个类中出现了该类的集合,通过Lombok的@Data注解生成class文件,当创建两个这个类的对象并且互相之间引用的时候,就出现了StackOverflowError异常,即栈溢出,或者叫超出栈深度.       
在Java虚拟机内存区域分为两种一种是线程共享区域,另一种是线程私有区域,而虚拟机栈就处在线程私有区域中,虚拟机栈描述的是Java方法执行的内存模型:每个方法被执行的时候都会同时创建一个栈帧,栈帧用于存储局部变量表,操作数栈,动态链接,方法返回地址等等。  

 

Java 虚拟机规范中,对这个区域规定了两种异常情况:
  • 如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常。
  • 如果虚拟机在动态扩展栈时无法申请到足够的内存空间,则抛出OutOfMemoryError异常。

下面给出个内存区域内存溢出的简单测试方法,

     回到文章讨论的问题,@Data注解在什么情况下可能导致StackOverflowError情况呢?先看下同学给出的示例代码,

@Data
public class Project {private Long id;private String projectName;private List<Project> projects;public static void main(String[] args) {Project project1 = new Project();Project project2 = new Project();project1.setProjects(Arrays.asList(project2));project2.setProjects(Arrays.asList(project1));System.out.println(project1.hashCode());}
}

从代码可以看出project1引用了project2,project2引用了project1,同学给出的解释如下:“ @Data 注解不仅帮我们实现了生成了getter/setter同时还重写了equals(Object other) 和 hashCode()方法, Lombok 会将 Project 类中的 List projects 当做是 hashCode 计算的一部分(同理,equals,toString 也会存在同样的问题),而如果我的项目中出现循环引用,这就会导致死循环,最终就会抛出 StackOverFlowError。”,

        难道相互引用就一定出现死循环吗?好吧,来看看反编译下@Data到底干啥了,代码反编译后确实发现hashCode()方法被Lombok自己生成了,代码如下,

public int hashCode() {int PRIME = true;int result = 1;Object $id = this.getId();int result = result * 59 + ($id == null ? 43 : $id.hashCode());Object $projectName = this.getProjectName();result = result * 59 + ($projectName == null ? 43 : $projectName.hashCode());Object $projects = this.getProjects();result = result * 59 + ($projects == null ? 43 : $projects.hashCode());return result;
}

 

那怎么就死循环了呢,其实问题在于projects是一个ArrayList集合,而ArrayList对hashCode() 的计算会把每一个元素拿出来调用元素的hashCode()求和,但是projects里面的元素是 project,project里面又有projects,因此出现无限递归调用,又是单线程中调用方法,所以就抛出StackOverflowError,在大多数场景中我们使用Lombok的@Data注解目的是为了生成getter/setter,不需要生成hashCode()和equals()方法,即使业务需要判断两个对象是否相等,逻辑基本也不会是lombok生成的那种,所以建议使用 @Getter 和 @Setter 替换 @Data注解

ArrayList的hashCode()代码如下:

public static int hashCode(Object a[]) {if (a == null)return 0;int result = 1;for (Object element : a)result = 31 * result + (element == null ? 0 : element.hashCode());return result;
}

 

 

这篇关于Lombok 工具中@Data注解生成hashCode()可能导致StackOverflowError情况的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1124637

相关文章

SpringBoot基于注解实现数据库字段回填的完整方案

《SpringBoot基于注解实现数据库字段回填的完整方案》这篇文章主要为大家详细介绍了SpringBoot如何基于注解实现数据库字段回填的相关方法,文中的示例代码讲解详细,感兴趣的小伙伴可以了解... 目录数据库表pom.XMLRelationFieldRelationFieldMapping基础的一些代

基于Python开发Windows自动更新控制工具

《基于Python开发Windows自动更新控制工具》在当今数字化时代,操作系统更新已成为计算机维护的重要组成部分,本文介绍一款基于Python和PyQt5的Windows自动更新控制工具,有需要的可... 目录设计原理与技术实现系统架构概述数学建模工具界面完整代码实现技术深度分析多层级控制理论服务层控制注

Spring的基础事务注解@Transactional作用解读

《Spring的基础事务注解@Transactional作用解读》文章介绍了Spring框架中的事务管理,核心注解@Transactional用于声明事务,支持传播机制、隔离级别等配置,结合@Tran... 目录一、事务管理基础1.1 Spring事务的核心注解1.2 注解属性详解1.3 实现原理二、事务事

k8s admin用户生成token方式

《k8sadmin用户生成token方式》用户使用Kubernetes1.28创建admin命名空间并部署,通过ClusterRoleBinding为jenkins用户授权集群级权限,生成并获取其t... 目录k8s admin用户生成token创建一个admin的命名空间查看k8s namespace 的

Java JDK Validation 注解解析与使用方法验证

《JavaJDKValidation注解解析与使用方法验证》JakartaValidation提供了一种声明式、标准化的方式来验证Java对象,与框架无关,可以方便地集成到各种Java应用中,... 目录核心概念1. 主要注解基本约束注解其他常用注解2. 核心接口使用方法1. 基本使用添加依赖 (Maven

Java 中的 equals 和 hashCode 方法关系与正确重写实践案例

《Java中的equals和hashCode方法关系与正确重写实践案例》在Java中,equals和hashCode方法是Object类的核心方法,广泛用于对象比较和哈希集合(如HashMa... 目录一、背景与需求分析1.1 equals 和 hashCode 的背景1.2 需求分析1.3 技术挑战1.4

基于Go语言开发一个 IP 归属地查询接口工具

《基于Go语言开发一个IP归属地查询接口工具》在日常开发中,IP地址归属地查询是一个常见需求,本文将带大家使用Go语言快速开发一个IP归属地查询接口服务,有需要的小伙伴可以了解下... 目录功能目标技术栈项目结构核心代码(main.go)使用方法扩展功能总结在日常开发中,IP 地址归属地查询是一个常见需求:

使用python制作一款文件粉碎工具

《使用python制作一款文件粉碎工具》这篇文章主要为大家详细介绍了如何使用python制作一款文件粉碎工具,能够有效粉碎密码文件和机密Excel表格等,感兴趣的小伙伴可以了解一下... 文件粉碎工具:适用于粉碎密码文件和机密的escel表格等等,主要作用就是防止 别人用数据恢复大师把你刚删除的机密的文件恢

Vue3 如何通过json配置生成查询表单

《Vue3如何通过json配置生成查询表单》本文给大家介绍Vue3如何通过json配置生成查询表单,本文结合实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录功能实现背景项目代码案例功能实现背景通过vue3实现后台管理项目一定含有表格功能,通常离不开表单

SpringBoot AspectJ切面配合自定义注解实现权限校验的示例详解

《SpringBootAspectJ切面配合自定义注解实现权限校验的示例详解》本文章介绍了如何通过创建自定义的权限校验注解,配合AspectJ切面拦截注解实现权限校验,本文结合实例代码给大家介绍的非... 目录1. 创建权限校验注解2. 创建ASPectJ切面拦截注解校验权限3. 用法示例A. 参考文章本文