【Java IO流】对象与字节流的序列化和反序列化

 

哈喽,哈喽,大家好~ 我是你们的老朋友:保护小周ღ  


今天给大家带来的是 【Java IO流】对象与字节流的序列化和反序列化,首先了解, 本次主题有啥实际应用, 学习 ByteArrayOutputStream / ByteArrayInputStream 字节数组流, ObjectOutputStream / ObjectInputStream 对象与字节流的序列化和反序列化 , 前者作为当期流的输出目的, 和数据来源,  一起来看看叭~

本期收录于博主的专栏JavaSE_保护小周ღ的博客-优快云博客

适用于编程初学者,感兴趣的朋友们可以订阅,查看其它 “JavaSE基础知识”。

更多精彩敬请期待:保护小周ღ *★,°*:.☆( ̄▽ ̄)/$:*.°★* ‘ 


对象与字节流的序列化和反序列化在 Java 开发中具有重要作用,主要用于将对象状态转换为字节流以便存储或传输。以下是它们的一些作用介绍:

作用

  1. 持久化存储:

    • 将对象的状态保存到文件或数据库,以便在系统重启时恢复。
    • 例如,将用户设置、游戏进度等信息保存到磁盘。
  2. 网络传输:

    • 在分布式系统或者微服务架构中,通过网络发送对象,以实现远程通信。
    • 例如,使用 RMI(远程方法调用)或者 gRPC 进行服务间调用时,需要传输对象。
  3. 深拷贝:

    • 创建对象的深拷贝,避免引用类型字段造成的共享问题。
    • 例如,在需要保护原始对象不被修改时,可以通过序列化和反序列化来复制对象。
  4. 缓存:

    • 将对象序列化后存储在内存中,减少重复计算,提高性能。
    • 例如,使用 Redis 等缓存工具时,可以序列化对象以便快速读取

一、处理字节数组的流类

ByteArrayOutputStreamByteArrayInputStream 是 Java 中用于处理字节数组的流类,通常用于输入和输出操作。

1.1 ByteArrayOutputStream

功能

  • ByteArrayOutputStream 允许你将数据写入一个字节数组中,而不是直接写入文件或其他输出流。
  • 这个类提供了一个可动态扩展的字节数组,可以随时获取当前写入的数据。

用法

  • 创建一个 ByteArrayOutputStream 流对象。
  • 使用 write() 方法将字节写入到流中。
  • 调用 toByteArray() 方法获取写入的字节数组。
public class ByteArrayOutputStreamExample {
    public static void main(String[] args) {
        // 这种将流对象写在 try 中的写法, 这种结构确保在操作完成后,相关的资源(如流)会被自动关闭,从而避免内存泄漏。
        try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
            String data = "Hello, World!";
            // getBytes() 方法就是将字符串转换为字节数组, 写入流对象
            bos.write(data.getBytes());

            // 获取字节数组
            byte[] byteArray = bos.toByteArray();
            System.out.println("Written bytes: " + new String(byteArray));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}


1.2 ByteArrayInputStream

功能

  • ByteArrayInputStream 允许你从一个字节数组中读取数据。
  • 可以用来在内存中模拟输入流操作,方便进行数据处理。

用法

  • 创建一个 ByteArrayInputStream 对象,并传入一个字节数组。
  • 使用 read() 方法读取字节,或者使用 read(byte[] b) 方法将数据读取到字节数组中。
public class ByteArrayInputStreamExample {
    public static void main(String[] args) {
        byte[] inputData = "Hello, World!".getBytes();

        try (ByteArrayInputStream bis = new ByteArrayInputStream(inputData)) {
            int data;
            int count = 0;
            while ((data = bis.read()) != -1) {
                count++;
                System.out.print((char) data); // 转换为字符并打印
            }
            System.out.println();
            // 说明一次读取四个字节, 但是也需要读取 13 次, Hello, World! 13个字符
            System.out.println(count);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}


 二、序列化与反序列化的流类

ObjectOutputStreamObjectInputStream 是 Java 中用于对象的序列化与反序列化的流类。这两个类使得将对象写入流并从流中读取对象变得非常简单。

注意:若是想一个类可具备序列化和反序列化的能力,需要该类实现 Serializable 接口。

  • 只序列化可序列化的成员变量。如果某个字段不需要序列化,可以使用 transient 关键字修饰该字段。
  • 如果类的结构发生变化(如修改了字段),可能会导致反序列化失败,这时可以使用 serialVersionUID 来版本控制。

2.1 ObjectOutputStream

功能

  • ObjectOutputStream 可用于将 Java 对象序列化为字节流,以便存储或通过网络传输。
  • 它将对象的状态(包括对象的属性)转换为字节序列。

用法

  1. 创建一个 FileOutputStream(文件流) 以指定输出目的地(如文件)。
  2. 创建一个 ByteArrayOutputStream(字节数组)  就可以输出到字节数组啦。
  3. 使用 ObjectOutputStream 将对象写入到流中。
  4. 调用 writeObject() 方法将对象序列化。
public class Person implements Serializable { //一定要实现Serializable 接口才可以序列化和反序列化
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{name='" + name + "', age=" + age + "}";
    }
}
public class ObjectOutputStreamExample {
    public static void main(String[] args) {
        try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); // 序列化后往哪个流里面写, 也可以是 FileOutputSteam
             ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream)) { // 这种写法可以自动关闭资源

            Person person = new Person("Alice", 30);
            objectOutputStream.writeObject(person); // 序列化对象
            System.out.println("Object serialized: " + person);
            // 存储在字节数据流中, 以字节数组的形式输出
            System.out.println(Arrays.toString(byteArrayOutputStream.toByteArray()));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}


2.2 ObjectInputStream

功能

  • ObjectInputStream 用于从字节流中反序列化对象,即读取序列化后的对象数据并恢复成 Java 对象。

用法

  1. 创建一个 FileInputStream (文件流) 指向序列化对象的文件。
  2. 创建一个 ByteArrayInputStream(字节数组)指向序列化对象的字节数组流。
  3. 使用 ObjectInputStream 从流中读取对象。
  4. 调用 readObject() 方法获取对象。
public class ObjectInputStreamExample {
    public static void main(String[] args) {
        // 用于存储 Person 对象序列化后字节
        byte[] personBytes = new byte[0];

        // 1. 先将 Person 对象序列化为字节数组
        try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
             ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream)) { // 这种写法可以自动关闭资源

            Person person = new Person("Alice", 30);
            objectOutputStream.writeObject(person); // 序列化对象
            System.out.println("Object serialized: " + person);
            // 存储在字节数据流中, 以字节数组的形式输出
            byte[] data = byteArrayOutputStream.toByteArray();
            personBytes = new byte[data.length];
            personBytes = byteArrayOutputStream.toByteArray();

            System.out.println(Arrays.toString(personBytes));
        } catch (Exception e) {
            e.printStackTrace();
        }

        // 2. 将字节数组personBytes, 反序列化为 person 对象
        try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(personBytes); // 从哪个字节数组中读取
             ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream)) {

            Person person = (Person) objectInputStream.readObject();// 反序列化对象
            System.out.println("----------------------------------");
            System.out.println("Object deserialized: " + person);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}


总结

  • 序列化:使用 ObjectOutputStream 将对象转化为字节流,可以存储或传输。
  • 反序列化:使用 ObjectInputStream 从字节流中恢复对象状态。

这两个类主要用于持久化对象,确保对象状态可以在不同的程序运行间保存和恢复。注意,参与序列化的对象必须实现 Serializable 接口。

且序列化/ 反序列化的字节流也可以使用 FileOutputStream 和 FileInputStream (文件流)来作为输出目的地和恢复的源头.


三、封装工具类

根据上述所讲, 我们可以将一个对象序列化为字节流, 也可以将字节流序列化为对象. 那我们就可以在开发中针对上述方法进行封装使用.

/**
 * Created with IntelliJ IDEA.
 * Description: 二进制序列化工具
 * Author: 保护小周
 * Date: 2024-08-21
 * Time: 23:22
 */
public class BinaryTool {

    /**
     * 把一个对象序列化成一个字节数组
     * @param object
     * @return
     */
    public static byte[] toBytes(Object object) throws IOException {
        // 这个流对象相当于一个变长的字节数组.
        // 就可以把 object 序列化的数据给逐渐的写入到 byteArrayOutputStream 中, 再统一转成 byte[]
        try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
            try (ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream)) {
                // 此处的 writeObject 就会把该对象进行序列化, 生成的二进制字节数据, 就会写入到
                // ObjectOutputStream 中.
                // 由于 ObjectOutputStream 又是关联到了 ByteArrayOutputStream, 最终结果就写入到 ByteArrayOutputStream 里了
                objectOutputStream.writeObject(object);
            }
            // 这个操作就是把 byteArrayOutputStream 中持有的二进制数据取出来, 转成 byte[]
            return byteArrayOutputStream.toByteArray();
        }
    }

    /**
     * 把一个字节数组, 反序列化成一个对象
     * @param data
     * @return
     */
    public static Object fromBytes(byte[] data) throws IOException, ClassNotFoundException {
        Object object = null;
        // 这个流对象相当于一个边长的字节数组
        // 就可以把object 序列化的数据给逐渐的写入到 byteArrayOutStream.
        try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(data)) {
            try (ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream)) {
                // 此处的 readObject 就是从 data 这个 byte[] 中读取数据并进行反序列化.
                 object = objectInputStream.readObject();
            }
        }
        return object;
    }
}

 实际应用效果展示: 非常的实用呀~


 好了,到这里,【Java IO流】对象与字节流的序列化和反序列化 博主已经分享完了,阐述较为基础,  希望对大家有所帮助,如有不妥之处欢迎批评指正。 

感谢每一位观看本篇文章的朋友,更多精彩敬请期待:保护小周ღ *★,°*:.☆( ̄▽ ̄)/$:*.°★* 

遇见你,所有的星星都落在我的头上……

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

保护小周ღ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值