Java序列化

最近遇到了一位同事咨询:Redis在存储数据的时候直接写入json字符串和序列化byte之后写入,产生的aof文件有没有大小差距?
这个问题咨询导师获得的答案是:两者产生的aof文件大小是一样的。
虽然知道了是因为在数据传输的时候字符会被转为byte数组再进行传输,但对这个过程是什么时候发生的,为什么要转换为byte进行传输还是存在疑问。周末对序列化进行了学习和总结。

1. 序列化是什么?

序列化就是将java对象转换为字节序列,反序列化当然就是将字节序列转换为java对象。
序列化的基本思路就是将对象转换为一组字节,然后将字节写入流中,可以使用ObjectOutputStream来序列化一个对象:

public static void serialize(Object obj, OutputStream out) throws IOException {
    ObjectOutputStream oos = new ObjectOutputStream(out);
    oos.writeObject(obj);
    oos.close();
}

2. 为什么要有序列化

先来看一个熟悉的生活场景: 在一般家庭里面通常会有一张可以坐下10-20个人的大圆桌,这张大圆桌通常只在人多的时候会被拿出来用,平时都是以桌板和桌腿的形式存放在杂物间。

这个场景中的大圆桌就和Java中的对象是非常相似的,它会在客厅(堆)中占据一个大空间,在不用的时候我们会把他拆卸((序列化))成桌面和桌腿等零件(字节序列)收到杂物间(磁盘)保存,在需要的时候再按照说明书(编码)拼装起来(反序列化)。我们还可能会搬家,在搬家的时候这张桌子也是以零件的形式运输的。

通过这个简单的例子,我们就能知道序列化的目的是为了更方便地存储和传输对象。

3. 如何序列化和解序列化

如果要让类可以被序列化,类需要实现Serializable接口。
Serializable接口时一个tag类的标记接口,没有需要实现的方法,唯一作用是告诉虚拟机和使用他的人:实现它的类是可以被序列化的。
在这1里插入图片描述

3.1 将序列化对象存储到文件

    public static void main(String[] args) {
        Person person1 = new Person("Ellie", 12);
        Person person2 = new Person("Pokia", 14);
        try (
                // 创建出FileOutputStream
                FileOutputStream fileOutputStream = new FileOutputStream("serialize_test.txt");
                //创建出ObjectOutStream
                ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream)
        ) {
            //写入对象
            objectOutputStream.writeObject(person1);
            objectOutputStream.writeObject(person2);
            //记得关闭ObjectOutPutStream和fileOutputStream,我把他放在try()里面,try块退出时,会自动调用资源的close()方法,关闭资源
        } catch (IOException ioException) {
            System.out.println("文件写入异常:" + ioException.getMessage());
        }
    }

3.2 将文件中序列化对象还原

    public static void main(String[] args) {
        try (
                //创建FileInputStream
                FileInputStream fileInputStream = new FileInputStream("serialize_test.txt");
                //创建ObjectInputStream
                ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream)
        ) {
            //读取对象
            Person person1 = (Person) objectInputStream.readObject();
            Person person2 = (Person) objectInputStream.readObject();
            //打印对象
            System.out.println(person1.toString());
            System.out.println(person2.toString());
        } catch (IOException exception) {
            System.out.println(exception.getMessage());
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
    }

3.3 将序列化对象保存到字节数组中和从字节数组中还原对象

        public static void main(String[] args) {
        Person person = new Person("Wigfrid", 16);
        byte[] bytes = person.serializeToByteArray();
        person.deSerializeFromByteArray(bytes);
    }

    /**
     * 序列化对象保存到字节数组中
     *
     * @return
     */
    private byte[] serializeToByteArray() {
        byte[] bytes = new byte[0];
        try (
                ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
                ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);

        ) {
            objectOutputStream.writeObject(this);
            bytes = outputStream.toByteArray();
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
        return bytes;
    }

    /**
     * 将字节数组还原成对象
     *
     * @param bytes
     */
    private void deSerializeFromByteArray(byte[] bytes) {
        try (
                ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes);
                ObjectInputStream objectInputStream = new ObjectInputStream(inputStream)
        ) {
            Person person = (Person) objectInputStream.readObject();
            System.out.println(person.toString());
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }

4. 序列化机制

Java 中的序列化机制是基于对象流的,即通过将对象写入到流中来实现序列化。Java 中的对象流包括 ObjectOutputStream 和 ObjectInputStream 两个类,分别用于将对象写入到流中和将对象从流中读取出来。
Java 中的序列化机制支持以下特性:

  • 对象引用:序列化时会将对象的引用序列化为一个标识符,反序列化时会根据这个标识符来恢复对象的引用关系。被引用的对象也必须是一个实现了序列化接口的类,否则会序列化失败
  • 继承关系:序列化时会将对象的类信息一并序列化,反序列化时会根据这个类信息来恢复对象的类关系
  • 版本控制:序列化时会将对象的版本号一并序列化,反序列化时会根据这个版本号来判断是否可以反序列化。如果对象的版本号与当前类版本不匹配,则抛出 InvalidClassException 异常
  • 同一对象序列化一次:所有保存到磁盘的对象都有一个序列化编号,当程序试图序列化一个对象时会先检查对象是否已经被序列化过,只有对象未被序列化过,才会将此对象序列化为字节序列输出,否则,直接输出编号即可。

5. 序列化的坑

  • 如果一些变量不能或者不应该被序列化,应该把他标记为transient(瞬时的)。在反序列化的时候,transient标记的标量他们的值不是null就是基本数据类型(primitive)的默认值
//不想被序列化的变量currentId
transient String currentId;
//需要被序列化的变量
String userName;
  • 静态对象不会被序列化
    静态对象static代表每个类一个,当对象被还原的时候,静态变量会维持类中原本的样子,而不是存储时的样子
  • 由于java序利化算法不会重复序列化同一个对象,只会记录已序列化对象的编号。如果序列化一个可变对象(对象内的内容可更改)后,更改了对象内容,再次序列化,并不会再次将此对象转换为字节序列,而只是保存序列化编号。

学习完《Head First Java》序列化部分,我明白了使用Jedis存储数据的时候直接写入json字符串和byte[]在传输的时候都是以byte序列形式传输到redis服务器的,既然传输的内容一致,持久化产生的aof文件大小也就没有差异,也找到了Jedis将字符串转为为byte[]发生的位置。
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值