最近在公司写代码的时候,碰上了一个问题:怎么把一个对象"存起来",下次还能原样读出来?
比如用户登录后,他的信息(用户名、ID、权限等)存在一个User
对象里。如果服务器重启了,这个对象就没了,用户又得重新登录------这体验太差了。
后来同事告诉我,用"序列化"就能解决这个问题。一开始我也听得一头雾水,什么序列化、反序列化,听着像科幻片里的术语。但用多了才发现,其实它挺简单的,而且特别实用。
今天我就用自己的话,讲讲什么是Java序列化,什么时候会用到它,还有反序列化是干啥的。
一、什么是Java序列化?
简单的说:把一个Java对象变成一串字节,存到文件里或者通过网络发出去,这个过程就叫序列化。
就像你拍张照片,可以把一个活生生的人"变成"一个数字文件,传给朋友看。序列化就是把内存里的对象,"拍成"一串可以存储或传输的字节流。
举个例子:
假设你有一个用户类:
java
public class User implements Serializable {
private String name;
private int age;
private String email;
// 构造方法、get/set省略
}
注意这个Serializable
接口。它是Java自带的,你只要让类实现它,这个类的对象就可以被序列化了。
然后你可以这样把它"存起来":
java
User user = new User("张三", 28, "zhangsan@email.com");
// 把对象写入文件
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("user.dat"));
oos.writeObject(user);
oos.close();
这时候你会发现项目目录下多了一个user.dat
文件。虽然你看不懂里面的内容(因为是字节),但它已经把"张三"这个对象完整地保存下来了。
二、什么时候需要序列化?
我总结了几个最常见的场景,都是我在项目里实际遇到的:
1. 把数据保存到本地文件
比如你写了个小工具,用户设置了一些偏好(主题颜色、字体大小等),你想下次打开时还能记住这些设置。
就可以把这些设置封装成一个对象,序列化存到本地。下次启动时反序列化读回来,用户的设置就"复活"了。
2. 网络传输对象
比如你做了一个客户端-服务器程序,客户端要传一个订单对象给服务器。
你不能直接把对象"扔"过去,得先序列化成字节流,通过网络发过去,服务器再反序列化还原成对象。
做聊天软件时,每条消息都是一个Message
对象,就是靠序列化发过去的。
3. 缓存对象(比如Redis)
虽然Redis常用JSON存数据,但有时候你不想折腾转JSON,可以直接把对象序列化后存进Redis。
比如用户登录后,把User
对象序列化存到Redis,设置个过期时间。之后每次请求直接从Redis读,不用查数据库,速度快。
4. 深拷贝对象
你可能知道对象赋值是"引用",改一个另一个也跟着变。
序列化+反序列化,可以快速实现一个"深拷贝":
java
// 把对象序列化再反序列化,相当于克隆了一个全新的对象
User user2 = (User) deserialize(serialize(user1));
这样 user2
和 user1
完全独立,改哪个都不影响另一个。
三、反序列化是啥?
反序列化就是序列化的"逆过程":把那一串字节重新变回Java对象。
继续上面的例子,你想读回"张三"的信息:
java
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("user.dat"));
User user = (User) ois.readObject();
ois.close();
System.out.println(user.getName()); // 输出:张三
看,对象又回来了!就像你从照片里"复活"了那个人。
四、要注意的坑
虽然序列化很方便,但也有些坑我们很容易踩:
1. 类必须实现 Serializable
不是所有类都能序列化。你的类必须 implements Serializable
,否则会报错。
java
public class User implements Serializable { ... }
2. serialVersionUID 最好手动指定
Java会自动给序列化类生成一个版本号。如果你改了类(比如加了个字段),再反序列化旧数据,可能会失败。
解决办法:手动加一个 serialVersionUID
:
java
public class User implements Serializable {
private static final long serialVersionUID = 1L;
// ...
}
这样即使类有小改动,也能尽量兼容旧数据。
3. 敏感字段要标记transient
比如密码、银行卡号这些不想被序列化的字段,加上transient
:
java
private transient String password;
这样序列化时会自动跳过这个字段,更安全。
五、小结:序列化就像"对象的快照"
序列化就是"对象 → 字节",反序列化就是"字节 → 对象"。 只要你想把对象存起来或发出去,就得请这对兄弟帮忙。
什么时候用?记住这几个关键词就行:
- 要保存对象状态
- 要跨网络传对象
- 要做深拷贝
- 要缓存复杂对象
下次你遇到类似需求,不妨试试序列化。几行代码,就能让对象"活"得更久。
本文首发于微信公众号「刘大华的开发笔记」我是大华,专注分享前后端开发的实战笔记。 关注我,少走弯路,一起进步!