序列化——谨慎实现Serializable
admin
2024-05-08 22:22:55
0

何为序列化

序列化与反序列化对应。

序列化:将一个对象转为字节流

反序列化: 将字节流转为对象

通过序列化继续,可以将对象编码之后,通过网络传输到其他应用中,再反序列化,实现远程交互调用。


如何序列化

要想使一个类的实例可以实例化,只需要实现 Serializable 即可。

正因为实现序列化太简单,很容易给程序员一种误解:序列化很容易,不需要考虑太多。

实际的情形却比较复杂。

标记类可实例化很简单,执行实例化动作的直接开销也很低。


序列化实例

我们先创建一个Car类

@Data
@Accessors(chain = true)
public class Car implements Serializable {private static final Long serialVersionUID = 1L;private String id;private Integer age;private String name;
}

创建序列化和反序列化方法

public class Client {public static void main(String[] args) throws Exception {
//        ser();dser();}/*** 序列化* @throws Exception*/static void ser() throws Exception {//age会序列化默认值nullCar car = new Car().setId("13").setName("小红");FileOutputStream fileOut =new FileOutputStream("C:\\Users\\admin\\Desktop\\file_upload\\car.ser");ObjectOutputStream out = new ObjectOutputStream(fileOut);out.writeObject(car);out.close();fileOut.close();System.out.println("--------序列化完成----------");}/*** 反序列化* @throws Exception*/static void dser() throws Exception {FileInputStream fileIn = new FileInputStream("C:\\Users\\admin\\Desktop\\file_upload\\car.ser");ObjectInputStream in = new ObjectInputStream(fileIn);Car car = (Car) in.readObject();in.close();fileIn.close();System.out.println("---------反序列化完成-------");System.out.println(car);}

如果需要特殊定制序列化, 这种情况,只有指定的字段被处理,其他的字段丢失。 注意,读写的顺序对应,否则值就串了

我们在Car类添加两个方法

@Data
@Accessors(chain = true)
public class Car implements Serializable {private static final Long serialVersionUID = 1L;private String id;private Integer age;private String name;private void writeObject(ObjectOutputStream out) throws IOException {System.out.println("writeObject");out.writeObject(id);out.writeInt(age == null? 1:age);out.writeObject(name);}private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {System.out.println("readObject");this.id= in.readObject().toString();this.age = in.readInt();this.name =in.readObject().toString();}
}

序列化的代价

1、一旦确认了序列化的形式。后续任何变动,都可能导致使用这个格式进行反序列化的程序发生错误。

2、默认的序列化会导出私有字段这一点与封装特性相违背

程序不同版本之间,因为优化或者修复 bug ,改变内部的私有字段非常常见

这些本来应该被隐藏在具体实现中,调用者不需要也不应该关心,但默认的序列化会导出这些数据

3、序列化是另一种创建对象实例的机制(clone同样也是绕过了构造器)

因为序列化绕过了构造器很容易发生“在构造器中做了安全前提检查,但因为序列化根本不走构造器,所以绕过了这种安全检查”


自定义序列化形式

假设,有个类 Cat 实现了序列化,并且将数据序列化,保存到硬盘或某个位置

之后,Cat 考虑应该继承一个 Animal 父类。并且,Animal 的信息自然也应该属于序列化的一部分

但注意:我们序列化的时候可没有保存 Animal 信息

这时候怎么办?

java 给出的方案就是 readObjectNoData

也就是,没有流,给一个默认的恢复方案。java 会调用 Animal 的 readObjectNoData

当然,还有一种类似的情况。就是本来就继承 Animal ,但Animal 忘了声明序列化。

在保存数据之后想起来了,加上了 Serializable ,但因为之前的数据中也没有 序列化父类信息

所以恢复的时候也应该从 readObjectNoData设置

总结: 此方法用在序列化的父类中。当子类序列化时候,父类未参与。

但后续反序列化的时候根据新规则,父类需要参与反序列化,可实际信息中,父类并没有,所以,需要用 readObjectNoData

回到刚才的话题,为什么需要书写 readObjectNoData?

因为,如果我们去掉这个方法,父类就默认不处理数据了,这时候可能处于错误的状态中

当然,如果父类不面临这种问题,就不需要处理

例子:

假设现在Car需要继承Animal

@Data
@Accessors(chain = true)
public class Animal{public int pid;
}
@Data
@Accessors(chain = true)
public class Car extends Animal implements Serializable {private static final Long serialVersionUID = 1L;private String id;private Integer age;private String name;private void writeObject(ObjectOutputStream out) throws IOException {System.out.println("writeObject");out.writeObject(id);out.writeInt(age == null? 1:age);out.writeObject(name);}private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {System.out.println("readObject");this.id= in.readObject().toString();this.age = in.readInt();this.name =in.readObject().toString();}
}

由于之前序列化的时候没有继承Animal,所以反序列化的时候,获取Animal的字段均为默认值,这个时候如果要自定义给Animal赋值就需要这样做

@Data
@Accessors(chain = true)
public class Animal implements Serializable {private static final Long serialVersionUID = 1L;public int pid;private void readObjectNoData() throws IOException, ClassNotFoundException {System.out.println("readObjectNoData");this.pid = 1;}
}

我们给父类的pid赋值1 ,然后调用反序列化方法

public class Client {public static void main(String[] args) throws Exception {
//        ser();dser();}/*** 序列化* @throws Exception*/static void ser() throws Exception {//age会序列化默认值nullCar car = new Car().setId("13").setName("小红");FileOutputStream fileOut =new FileOutputStream("C:\\Users\\admin\\Desktop\\file_upload\\car.ser");ObjectOutputStream out = new ObjectOutputStream(fileOut);out.writeObject(car);out.close();fileOut.close();System.out.println("--------序列化完成----------");}/*** 反序列化* @throws Exception*/static void dser() throws Exception {FileInputStream fileIn = new FileInputStream("C:\\Users\\admin\\Desktop\\file_upload\\car.ser");ObjectInputStream in = new ObjectInputStream(fileIn);Car car = (Car) in.readObject();in.close();fileIn.close();System.out.println("---------反序列化完成-------");System.out.println(car);System.out.println(car.pid);}
}

可以看到,父类的pid字段被成功赋值 

readObjectNoData
readObject
---------反序列化完成-------
Car(id=13, age=1, name=小红)


补充说明

以上的情况是出于谨慎考虑,实际上, 现在很多框架都不会明确依赖序列化

更多的是Object - json 的路线或者使用 rest api

现在的 SpringBoot 或 SpringCloud 项目,一般不会被序列化问题所困扰

相关内容

热门资讯

机器人概念活跃!603331,... 今日早盘,受隔夜外盘大跌影响,A股主要股指普遍低位运行。截至午间休市,上证指数报4105.04点,跌...
时隔4年,日本当局再次扣押中国... 在中日关系持续紧张的背景下,日方在争议海域对中方渔船采取激进行动。 据日本共同社2月13日报道,日...
“A+H”双重上市!坪山这家企... 2月13日,深圳市沃尔核材股份有限公司(以下简称“沃尔核材”,港股代码:09981.HK;A股代码:...
抗抑郁药物到底怎么选?整理总结... 作品声明:内容仅供参考,如有不适及时就医 抑郁症作为一种常见的精神疾病,影响着全球数以亿计的人口。...
“市场看得见、摸不着”,中国光... 界面新闻记者|马悦然 界面新闻编辑 | 张慧 杨悦 “市场还是过于乐观了。” 在太空光伏火爆的几...
原创 3... 在你开始阅读这篇文章之前,不妨点个关注,这样不仅可以方便讨论和分享,也能带给你不一样的参与感。感谢你...
克林根23+18霍勒迪31+9... 【搜狐体育战报】北京时间2月13日NBA常规赛,客场作战的开拓者以135-119击败爵士。霍勒迪31...
台媒:台积电营收创单月历史新高 参考消息网2月13日报道据台湾CMoney投资网站2月11日报道,台积电公布2026年1月合并营收首...
从房产到AI,风险全面引爆,美... 对人工智能将颠覆众多行业商业模式的深层忧虑,叠加美国成屋销售降幅创四年来最大,引发市场避险情绪 文|...
又一家2万亿级AI独角兽诞生 2026.02.13 本文字数:1558,阅读时长大约3分钟 作者 |第一财经 刘晓洁 封图 |A...
商务部:2月13日起对原产于欧... 2月12日,商务部公布对原产于欧盟的进口相关乳制品反补贴调查的最终裁定。 商务部称,根据《中华人民共...
卖掉高层的33楼,搬回老小区“... 大家好,在进入正文之前,给大家做一个推荐。 我的一个设计师好友夏夏,从LXD离职后,经历过设计创业的...
原创 只... 近期,一份由法国相关咨询机构提交的贸易攻略引发关注。该报告建议对特定东方商品加征百分之三十的整体关税...
黄金大消息!深圳出手了:严禁使... 来源:深圳市地方金融管理局网站 为进一步规范深圳市黄金市场经营秩序,防范化解市场风险,保护消费者合法...
两度播报|岚图汽车2025年毛... 2026年2月12日,岚图汽车(07489.HK)完成港股上市的全部前置监管审批流程并更新招股书,上...
从400万元到2500万元:一... 记者 蔡越坤 2026年的第一声资本惊雷,并非源于人工智能(AI)或是机器人等科技创新,而是来自黄金...
从xAI联创“转身”看行业局势... 扬帆出海 作者丨以南 短短两天时间,xAI炸锅了。 2026年2月10日至11日,两位联合创始人吴宇...
“全球大模型第一股”智谱股价创... IT之家 2 月 13 日消息,今日早盘,智谱港股盘初快速拉升,盘中最高涨超 22%,再创上市以来新...
大麦娱乐入股香港快达票,联手开... 2月13日,大麦娱乐宣布与香港票务平台快达票达成战略投资合作。根据合作安排,大麦娱乐将入股快达票,双...