这是我参与更文挑战的第6天,活动详情查看: 更文挑战
对于序列化的认知一直是便于网络传输,不清楚其中原理,同时发现之前有项目entity并没有显示调用Serializable.
什么是序列化
- 序列化:把Java对象转换为字节序列。
- 反序列化:把字节序列恢复为原先的Java对象。
为什么需要序列化
便于对象持久化和传输。
如何序列化
Java中想要序列化一个对象,必须实现Serializable接口。然后就可以持久化和反序列化了。
创建表a,b
-- Table structure for a
-- ----------------------------
DROP TABLE IF EXISTS `a`;
CREATE TABLE `a` (
`id` int(4) NOT NULL AUTO_INCREMENT,
`name` varchar(10) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
`address` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
`create_date` datetime(0) DEFAULT NULL,
`remark` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 12 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of a
-- ----------------------------
INSERT INTO `a` VALUES (10, '大明', '广东', '2021-06-05 19:18:31', 'hi');
INSERT INTO `a` VALUES (11, '大明', '广东', '2021-06-05 19:18:31', 'hi');
SET FOREIGN_KEY_CHECKS = 1;
复制代码
使用插件生成基础代码
参考IDEA插件之EasyCode MybatisCodeHelper
去掉B的implements Serializable,Entity A,B如下:
public class A implements Serializable {
private static final long serialVersionUID = -82776330359794256L;
private Integer id;
private String name;
private String address;
private Date createDate;
private String remark;
复制代码
public class B{
private Integer id;
private String name;
private String address;
private Date createDate;
private String remark;
复制代码
分别调用A,B的保存方法
显示调用实现Serializable接口现在看起来没什么用,进去看下源码只有一个空接口
* @author unascribed
* @see java.io.ObjectOutputStream
* @see java.io.ObjectInputStream
* @see java.io.ObjectOutput
* @see java.io.ObjectInput
* @see java.io.Externalizable
* @since JDK1.1
*/
public interface Serializable {
}
复制代码
查看源码
源码中数据类型已经默认实现了Serializable
那么我们为什么还要显示调用Serializable?
反序列化
@SpringBootTest
class DemoApplicationTests {
@Test
void contextLoads() {
A a = new A();
try {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("G:\\userA.txt"));
objectOutputStream.writeObject(a);
objectOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
@Test
void contextLoads2() {
B b= new B();
try {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("G:\\userB.txt"));
objectOutputStream.writeObject(b);
objectOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
复制代码
反序列化A时没有问题,反序列化B时异常
逐步进去看下错误
反序列化时obj instanceof Serializable
判断,Serializable只是作为一个标识使用
为什么要声明serialVersionUID
类的serialVersionUID的默认值完全依赖于Java编译器的实现,对于同一个类,用不同的Java编译器编译,有可能会导致不同的serialVersionUID。显式地定义serialVersionUID有两种用途:
1.希望类的不同版本对序列化兼容,因此需要确保类的不同版本具有相同的serialVersionUID;在某些场合,不希望类的不同版本对序列化兼容,因此需要确保类的不同版本具有不同的serialVersionUID。
2.当你序列化了一个类实例后,希望更改一个字段或添加一个字段,不设置serialVersionUID,所做的任何更改都将导致无法反序化旧有实例,并在反序列化时抛出一个异常。
如果你添加了serialVersionUID,在反序列旧有实例时,新添加或更改的字段值将设为初始化值(对象为null,基本类型为相应的初始默认值),字段被删除将不设置。
小结
1.便于对象持久化和传输。
2.Serializable是一个空接口,只作为一个标识
3.当一个父类实现序列化,子类自动实现序列化,不需要显式实现Serializable接口