关于对于Java中Entity以及VO,以及DTO中Request对象序列化的学习
关于 Serializable的探讨
前提引入
是由于软件测试上有同学提到说,什么该字段在程序刚运行时,导致jvm激增,所以吸引了我的注意
回顾代码
MybatisPlus Generator自动生成的entity中就经常带有这个,
而且我在开发代码的时候VO,以及DTO常常是直接复制对应的entity,所以也保不齐我对应的VO等对象也保留了这个(惭愧表情包),印象中之前就学过,不过忘得差不多了,所以就于此复习一下
问题
- 为什么要有 Serializable
- 其中的private static final long serialVersionUID = 1L;又有什么用?
- 跟序列化有关?是跟具体的某种序列化有关?还是全部?
回答
one,two
在Java中,implements Serializable和serialVersionUID是与对象序列化(serialization)相关的概念,特别是在需要将对象的状态持久化、传输或者缓存时会用到。下面详细解释这些概念以及在MyBatis-Plus中它们的使用场景。
- Serializable 接口
Serializable 是一个标记接口(marker interface),没有任何方法定义,它的存在只是为了告诉JVM某个类的实例是可以被序列化的。序列化的主要用途包括:
持久化:将对象的状态保存到一个存储介质(如文件、数据库)中,以便后续可以恢复。
传输:通过网络传输对象,比如在分布式系统中,从一个JVM传递到另一个JVM。
缓存:将对象放入缓存中,以便于快速恢复对象状态。
- serialVersionUID 字段
serialVersionUID 是一个静态的、最终的(final)长整型字段,用于标识序列化的版本。其作用如下:
版本控制:在反序列化时,JVM会检查传入的字节流中的serialVersionUID是否与本地对应类的serialVersionUID一致。如果一致,说明序列化的版本是兼容的,可以安全地进行反序列化。如果不一致,会抛出InvalidClassException,防止版本不兼容带来的问题。
手动定义serialVersionUID可以避免某些情况下因类的微小变动(如增加一个方法或字段)导致的反序列化失败。一个常见的定义方式是:
private static final long serialVersionUID = 1L;
three
对于不同的序列化机制,如JSON序列化、数据库存储以及其他的自定义序列化方案,serialVersionUID 并不起作用。下面详细讨论serialVersionUID在不同场景中的作用和局限性,以及其他场景中的序列化方式。
- JDK 自带的序列化器
serialVersionUID 是Java内置的序列化机制的一部分,特别是用于 ObjectInputStream 和 ObjectOutputStream 时起作用。当使用这些流进行对象序列化和反序列化时,JVM会检查类的 serialVersionUID 是否匹配,以确保类的版本一致性。 - JSON 序列化
在使用JSON(如通过 Gson、Jackson 或者其他库)进行序列化和反序列化时,serialVersionUID 并不起作用。这是因为JSON序列化器将对象转换为JSON字符串,并不保留Java类的版本信息。因此,在这种情况下,不需要 serialVersionUID。例如:
import com.fasterxml.jackson.databind.ObjectMapper;
public class User {
private String name;
private int age;
// Getters and Setters
}
ObjectMapper objectMapper = new ObjectMapper();
User user = new User();
user.setName("John");
user.setAge(30);
// 序列化
String jsonString = objectMapper.writeValueAsString(user);
// 反序列化
User user2 = objectMapper.readValue(jsonString, User.class);
- Redis 存储
当使用Redis进行数据存储时,通常也会使用JSON字符串进行序列化和反序列化。因为Redis是一个键值存储,保存的是序列化后的数据字符串,而不是Java对象本身,因此 serialVersionUID 并不起作用。 - 数据库存储
对于MySQL等关系型数据库,当对象被存储时,ORM(如MyBatis、Hibernate)会将对象的字段提取出来并生成相应的SQL语句进行存储和查询。在这种情况下,对象序列化是由ORM框架处理的,serialVersionUID 也并不起作用。
serialVersionUID 的适用场景
综上所述,serialVersionUID 的适用场景主要局限于Java内置的序列化机制。当你在分布式系统中使用Java对象的原生序列化和反序列化时,serialVersionUID 可以确保不同版本的类之间的兼容性。如果你的应用程序不使用Java内置的序列化机制,而是使用JSON、XML或其他格式进行序列化,那么 serialVersionUID 并不需要关注。
举个我在项目中遇到的例子
CountMinSketch
当时我在实现一个OJ系统,其中有个类似github,leetcode的提交记录等等的情况,我是懒得放到个人主页,于是我直接放到首页中,其中对应的数据该在后端中怎么存呢?
我联想到了bitmap,可惜他只能是二值,而我们需要保留提交记录中一天提交了多少次呀,所以是不可行的,那么bitmap不行,没有其他的数据结构能同样省空间了吗?有那就CountMinSketch,但是他是概率数据结构,也就是说可能会有误差(也就是误差率以及误差距离越小那么消耗更多的空间,底层思路实现和bloomfilter类似)
这是我当时写的小测试:
public class TestCountMinScratch {
public static void main(String[] args) {
CountMinSketch sketch = new CountMinSketch(0.001, 0.99, 1);
// 对数据进行更新
sketch.add("2024-5-16",1);
sketch.add("2024-5-16",1);
sketch.add("2024-5-16",1);
// 查询频率
long frequency = sketch.estimateCount("2024-5-16");
System.out.println("Frequency of 'example': " + frequency);
}
}
这个是当时最后没用上的代码,最后选择用了hash结合一定的编码来进行处理,考虑到通常展示的365个天数的提交记录应该也不会很耗时间,又能具有准确性
下面是之前使用CountMinSketch的代码:
@Component
public class CountMinSketchFactory {
/**
* 误差率:确保与最大为真实值*(1+epsOfTotalCount)
* 最小同理
*/
private final static double epsOfTotalCount=0.07;
/**
* 置信度:置信度为 0.99 意味着我们希望在 99% 的情况下,查询估计的误差在指定的误差率范围内。
* 也就是99%的情况在上面我们推出的范围中
*/
private final static double confidence=0.99;
/**
* 在随机数生成和某些概率数据结构中(如 CountMinSketch),种子(seed) 是一个初始值,用于初始化随机数生成器或哈希函数。它的作用是确保随机过程在相同的种子下每次运行都产生相同的结果。
*/
private final static int seed=1;
@Autowired
private BitmapRedisTemplate bitmapRedisTemplate;
public CountMinSketch getCountMinSketch(Long uid) {//todo:这里是否适合双检加锁?
String s = bitmapRedisTemplate.opsForValue().get(RedisConstant.USER_COMMIT_STATICS + uid);
if(s==null) {
return new CountMinSketch(epsOfTotalCount, confidence, seed);
}else{
return JSONUtil.toBean(s, CountMinSketch.class);
}
}
public void storeCountMinSketch(Long uid,CountMinSketch countMinSketch) {
bitmapRedisTemplate.opsForValue().set(RedisConstant.USER_COMMIT_STATICS + uid, JSONUtil.toJsonStr(countMinSketch));
}
}
对于这个情况我原本是打算使用redis来存储的,毕竟是微服务项目,于是我开始两个方案进行对比,分别是项目应用场景1年内的情况以及放大到10年内的提交次数,在序列化到redis之后,却发现countminSketch没有任何变化,原先我还觉得这数据结构这么厉害,结果打开数据一看,什么都没有,这可能也正是json序列化与jdk序列化的区别吧,而且在这次测试中hash在时间消耗上差距并不大,所以选用了hash
最后回到前提
每个serialVersionUID都是静态且final修饰,而且他们也不会被GC所清理,而且消耗空间也不会特别大,除非类爆炸现象,可能当时我没注意听,不然这个现象不可能是一个软件测试的问题,对了有必要的话,还是保留着,毕竟难免可能之后会用到
最后再提一嘴像那些dto什么的以及vo什么的就不需要了,因为你根本不会用到把他们当做一个对象传
关于对于Java中Entity以及VO,以及DTO中Request对象序列化的学习的更多相关文章
- java项目中VO和DTO以及Entity,各自是在什么情况下应用的
j2ee中,经常提到几种对象(object),理解他们的含义有助于我们更好的理解面向对象的设计思维. POJO(plain old java object):普通的java对象,有别于特殊的j ...
- java项目中VO、DTO以及Entity,各自是在什么情况下应用的
按照标准来说: entity里的每一个字段,与数据库相对应 vo里的每一个字段,是和你前台页面相对应 dto,这是用来转换从entity到dto,或者从dto到entity的中间的东西 举个例子: h ...
- Java 之 Request 对象
一.Request 对象和 Response 对象原理 request和response对象是由服务器创建的,供我们使用的. request对象是来获取请求消息,response对象是来设置响应消息. ...
- java项目中VO和DTO以及Entity,各自是在什么情况下应用
1.entity里的每一个字段,与数据库相对应, 2.dto里的每一个字段,是和你前台页面相对应, 3.VO,这是用来转换从entity到dto,或者从dto到entity的中间的东西. 举个例子 ...
- Java Bean、POJO、 Entity、 VO 、PO、DAO
Java Bean.POJO. Entity. VO , 其实都是java 对象,只不过用于不同场合罢了. Java Bean: 就是一个普通的Java 对象, 只不过是加了一些约束条件. 声 ...
- 当实体类中entity/DTO/VO等类中,有枚举值,应该怎么输出?
当实体类中entity/DTO/VO等类中,有枚举值,应该怎么输出? 问题: orderStatus 和 payStatus都是枚举类,并且枚举的个数达地10来个,我们不可能在模板页面(jsp/ftl ...
- Java中PO、BO、VO、DTO、POJO、DAO概念及其作用和项目实例图(转)
PO(bean.entity等命名): Persistant Object持久对象,数据库表中的记录在java对象中的显示状态 最形象的理解就是一个PO就是数据库中的一条记录. 好处是可以把一条记录作 ...
- java中的vo 、dto 、dao--转
原文地址:http://yinchunjian.iteye.com/blog/758196 O是跟数据库里表的映射,一个表对应一个VO DAO是用VO来访问真实的表,对数据库的操作都在DAO中完成 B ...
- java中的vo、dto 、dao
VO是跟数据库里表的映射,一个表对应一个VO DAO是用VO来访问真实的表,对数据库的操作都在DAO中完成 BO是业务层,做逻辑处理的 VO , PO , BO , QO, DAO ,POJO ...
- java 中的VO,PO,DTO,DO对象
经常会接触到VO,DO,DTO的概念,本文从领域建模中的实体划分和项目中的实际应用情况两个角度,对这几个概念进行简析. 得出的主要结论是:在项目应用中,VO对应于页面上需要显示的数据(表单),DO对应 ...
随机推荐
- IE8发送ajax请求无效
IE是个非常有个性的浏览器,常规的东西在他这个都不太好使. 最开始发送ajax请求,总是不成功,也没啥报错,反正就是请求被忽略了 然后我就考虑用原生的JS来实现,然后就:哎呀 可以了...... x ...
- Blazor OIDC 单点登录授权实例7 - Blazor hybird app 端授权
目录: OpenID 与 OAuth2 基础知识 Blazor wasm Google 登录 Blazor wasm Gitee 码云登录 Blazor OIDC 单点登录授权实例1-建立和配置IDS ...
- MogDB/openGauss学习笔记-获取对象DDL
MogDB/openGauss 学习笔记-获取对象 DDL 本文出处:https://www.modb.pro/db/399230 内置函数 omm2=# \df *def List of funct ...
- 开源相机管理库Aravis例程学习(二)——连续采集multiple-acquisition-main-thread
目录 简介 例程代码 函数说明 arv_camera_set_acquisition_mode arv_camera_create_stream arv_camera_get_payload arv_ ...
- 重新点亮linux 命令树————screen 命令和系统日志[二十四]
前言 简单介绍一下screen 正文 因为我们终端关闭后,终端就消失了,故而希望有终端保持. 1.yum install screen 进行安装. 2.使用screen 进行进入 3.然后打开tail ...
- 如何使用Plotly和Dash进行数据可视化
本文分享自华为云社区<从数据到部署使用Plotly和Dash实现数据可视化与生产环境部署>,作者: 柠檬味拥抱. 数据可视化是数据分析中至关重要的一环,它能够帮助我们更直观地理解数据并发现 ...
- Spring开发:动态代理的艺术与实践
本文分享自华为云社区<Spring高手之路17--动态代理的艺术与实践>,作者: 砖业洋__. 1. 背景 动态代理是一种强大的设计模式,它允许开发者在运行时创建代理对象,用于拦截对真实对 ...
- JDBC数据库汇总Attack研究
前言 针对除Mysql的其它数据库的jdbc attack分析 H2 RCE 介绍 H2 是一个用 Java 开发的嵌入式数据库,它本身只是一个类库,即只有一个 jar 文件,可以直接嵌入到应用项目中 ...
- 力扣202(java&python)-快乐数(简单)
题目: 编写一个算法来判断一个数 n 是不是快乐数. 「快乐数」 定义为: 对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和.然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终 ...
- 力扣224(java)-基本计算器(困难)
题目: 给你一个字符串表达式 s ,请你实现一个基本计算器来计算并返回它的值. 注意:不允许使用任何将字符串作为数学表达式计算的内置函数,比如 eval() . 示例 1: 输入:s = " ...