Java之Serializable

这是我参与更文挑战的第6天,活动详情查看: 更文挑战

对于序列化的认知一直是便于网络传输,不清楚其中原理,同时发现之前有项目entity并没有显示调用Serializable.

什么是序列化

  1. 序列化:把Java对象转换为字节序列。
  2. 反序列化:把字节序列恢复为原先的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

image.png

去掉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的保存方法

image.png

image.png

显示调用实现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 {
}
复制代码

查看源码

image.png

image.png

源码中数据类型已经默认实现了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时异常

image.png

逐步进去看下错误

image.png

image.png

image.png

反序列化时obj instanceof Serializable判断,Serializable只是作为一个标识使用

为什么要声明serialVersionUID

类的serialVersionUID的默认值完全依赖于Java编译器的实现,对于同一个类,用不同的Java编译器编译,有可能会导致不同的serialVersionUID。显式地定义serialVersionUID有两种用途:

1.希望类的不同版本对序列化兼容,因此需要确保类的不同版本具有相同的serialVersionUID;在某些场合,不希望类的不同版本对序列化兼容,因此需要确保类的不同版本具有不同的serialVersionUID。
2.当你序列化了一个类实例后,希望更改一个字段或添加一个字段,不设置serialVersionUID,所做的任何更改都将导致无法反序化旧有实例,并在反序列化时抛出一个异常。
如果你添加了serialVersionUID,在反序列旧有实例时,新添加或更改的字段值将设为初始化值(对象为null,基本类型为相应的初始默认值),字段被删除将不设置。

小结

1.便于对象持久化和传输。
2.Serializable是一个空接口,只作为一个标识
3.当一个父类实现序列化,子类自动实现序列化,不需要显式实现Serializable接口

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享