【Java 基础篇】Java对象反序列化流详解

2023-09-19 22:21:56

在这里插入图片描述

在Java编程中,对象序列化和反序列化是常见的操作,用于将对象转换为字节流以便于存储或传输,并从字节流中重新构建对象。本文将重点介绍对象反序列化流的用法和相关概念,帮助基础小白理解这一重要的主题。

什么是对象反序列化?

对象反序列化是将之前序列化的对象字节流还原为对象的过程。这个过程是序列化的逆过程,它可以让我们重新获得原始的Java对象,包括对象的状态和数据。反序列化是一种重要的机制,用于在Java中实现数据的持久化和跨网络通信。

对象反序列化的核心类是ObjectInputStream,它提供了一种方法来读取已序列化的对象数据并将其还原为Java对象。

ObjectInputStream的基本用法

要使用ObjectInputStream,首先需要创建一个输入流并将其连接到包含序列化对象的数据源,通常是一个文件或网络连接。接下来,您可以使用ObjectInputStream来读取对象。

下面是一个基本的对象反序列化示例:

import java.io.*;

public class ObjectDeserializationExample {
    public static void main(String[] args) {
        try {
            // 创建一个输入流,连接到包含序列化对象的文件
            FileInputStream fileIn = new FileInputStream("serializedObject.ser");
            ObjectInputStream in = new ObjectInputStream(fileIn);

            // 使用ObjectInputStream读取对象
            MyClass deserializedObject = (MyClass) in.readObject();

            // 关闭流
            in.close();
            fileIn.close();

            // 使用反序列化后的对象
            System.out.println("Deserialized Object: " + deserializedObject);
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

上述示例中,我们使用FileInputStream将对象反序列化流连接到一个包含序列化对象的文件。然后,我们使用ObjectInputStreamreadObject方法来读取对象,并将其强制转换为原始的Java对象。

Serializable接口和版本控制

在进行对象序列化和反序列化时,需要确保被操作的类实现了Serializable接口。这个接口是一个标记接口,没有定义任何方法,但它告诉Java运行时系统这个类可以进行序列化。

import java.io.Serializable;

public class MyClass implements Serializable {
    // 类的成员和方法
}

另一个重要的考虑因素是版本控制。当您对一个已序列化的类进行更改时,特别是在类的字段或结构发生变化时,可能会导致版本不兼容。为了处理版本兼容性问题,可以在类中显式定义serialVersionUID,如下所示:

private static final long serialVersionUID = 123456789L;

通过显式定义serialVersionUID,您可以确保在反序列化时可以与之前的版本兼容。如果没有提供serialVersionUID,Java会根据类的结构自动生成版本号,这可能会导致反序列化失败。

自定义序列化与writeObjectreadObject方法

有时,您可能需要自定义对象的序列化和反序列化过程,以满足特定需求。您可以在类中定义以下两个方法:

  • private void writeObject(ObjectOutputStream out) throws IOException:这个方法在对象序列化时自动被调用。您可以在其中编写自定义的序列化逻辑。
  • private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException:这个方法在对象反序列化时自动被调用。您可以在其中编写自定义的反序列化逻辑。

这两个方法的签名必须与上述示例中的一致。

序列化的性能和安全性考虑

尽管对象序列化和反序列化是强大的工具,但在性能和安全性方面需要谨慎。以下是一些性能和安全性方面的考虑:

性能考虑

  • 序列化和反序列化可能是昂贵的操作,特别是对大型对象或大量对象的处理。要谨慎使用它们,以避免性能问题。
  • 考虑使用更轻量级的序列化格式,如JSON或Protocol Buffers,以提高性能。

安全性考虑

  • 反序列化操作可能存在安全风险,因为恶意用户可以创建恶意的序列化数据。要确保只反序列化来自受信任源的数据,并对反序列化的数据进行有效验证。
  • 考虑使用安全的序列化机制,如Java的序列化过滤器或自定义的反序列化控制,以减少安全风险。

常用示例

当涉及对象反序列化时,通常有以下几个常见的应用场景。以下是一些示例:

1. 从文件中加载配置数据

假设您的应用程序需要读取和加载配置数据,您可以使用对象序列化来将配置对象保存到文件中。然后,在应用程序启动时,您可以使用对象反序列化从文件中加载配置数据。这可以帮助您在不更改代码的情况下轻松更改和管理配置。

// 序列化配置数据到文件
public static void serializeConfiguration(Configuration config) {
    try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("config.ser"))) {
        out.writeObject(config);
    } catch (IOException e) {
        e.printStackTrace();
    }
}

// 从文件中反序列化配置数据
public static Configuration deserializeConfiguration() {
    try (ObjectInputStream in = new ObjectInputStream(new FileInputStream("config.ser"))) {
        return (Configuration) in.readObject();
    } catch (IOException | ClassNotFoundException e) {
        e.printStackTrace();
        return null;
    }
}

2. 缓存对象

有时,您可能希望将一些对象缓存到磁盘上,以便稍后重新加载它们,而不是每次都重新生成它们。对象序列化和反序列化可用于实现此功能。

// 序列化对象到缓存文件
public static void serializeToCache(Object object, String cacheKey) {
    try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(cacheKey))) {
        out.writeObject(object);
    } catch (IOException e) {
        e.printStackTrace();
    }
}

// 从缓存文件中反序列化对象
public static Object deserializeFromCache(String cacheKey) {
    try (ObjectInputStream in = new ObjectInputStream(new FileInputStream(cacheKey))) {
        return in.readObject();
    } catch (IOException | ClassNotFoundException e) {
        e.printStackTrace();
        return null;
    }
}

3. 跨网络传输对象

在分布式系统中,您可能需要将对象从一个地方传输到另一个地方。对象序列化可用于将对象转换为字节流,并在网络上传输,然后在接收端进行反序列化。

// 服务器端 - 序列化并发送对象
try (ServerSocket serverSocket = new ServerSocket(12345)) {
    while (true) {
        Socket clientSocket = serverSocket.accept();
        ObjectOutputStream out = new ObjectOutputStream(clientSocket.getOutputStream());
        out.writeObject(myObject);
        out.close();
        clientSocket.close();
    }
} catch (IOException e) {
    e.printStackTrace();
}

// 客户端 - 接收并反序列化对象
try (Socket socket = new Socket("server-hostname", 12345)) {
    ObjectInputStream in = new ObjectInputStream(socket.getInputStream());
    MyObject receivedObject = (MyObject) in.readObject();
    in.close();
} catch (IOException | ClassNotFoundException e) {
    e.printStackTrace();
}

4. 数据持久化

对象序列化还可以用于数据持久化,特别是在应用程序需要长期存储和恢复数据时。例如,您可以使用对象序列化将用户的应用程序状态保存在文件中,以便在下一次启动应用程序时恢复该状态。

// 序列化应用程序状态到文件
public static void serializeAppState(AppState state) {
    try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("appstate.ser"))) {
        out.writeObject(state);
    } catch (IOException e) {
        e.printStackTrace();
    }
}

// 从文件中反序列化应用程序状态
public static AppState deserializeAppState() {
    try (ObjectInputStream in = new ObjectInputStream(new FileInputStream("appstate.ser"))) {
        return (AppState) in.readObject();
    } catch (IOException | ClassNotFoundException e) {
        e.printStackTrace();
        return null;
    }
}

5. 消息传递

在分布式系统中,消息传递是一种常见的通信方式。对象序列化和反序列化可用于将消息封装为对象,并在系统的不同部分之间传递消息。

// 发送方 - 序列化消息并发送
Message message = new Message("Hello, world!");
try (Socket socket = new Socket("server-hostname", 12345)) {
    ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream());
    out.writeObject(message);
    out.close();
} catch (IOException e) {
    e.printStackTrace();
}

// 接收方 - 接收并反序列化消息
try (ServerSocket serverSocket = new ServerSocket(12345)) {
    while (true) {
        Socket clientSocket = serverSocket.accept();
        ObjectInputStream in = new ObjectInputStream(clientSocket.getInputStream());
        Message receivedMessage = (Message) in.readObject();
        in.close();
        clientSocket.close();
        // 处理接收到的消息
    }
} catch (IOException | ClassNotFoundException e) {
    e.printStackTrace();
}

这些示例涵盖了对象反序列化的几个常见用途场景,包括配置管理、对象缓存、跨网络传输、数据持久化和消息传递。通过对象序列化,您可以在不同的上下文中轻松地传输、存储和加载对象数据。

总结

对象反序列化是Java中重要的编程概念,用于将序列化的对象还原为原始的Java对象。通过了解ObjectInputStream的基本用法、Serializable接口、版本控制、自定义序列化和性能、安全性考虑,您可以更好地使用和理解对象反序列化流。但请谨慎使用它,特别是在面临性能和安全性问题时。

更多推荐

小谈设计模式(3)—策略模式

小谈设计模式(3)—策略模式专栏介绍专栏地址专栏介绍策略模式主要角色环境(Context)抽象策略(Strategy)具体策略(ConcreteStrategy)角色总结核心思想封装算法定义抽象策略使用环境类思想总结Java代码实现——以一个游戏角色攻击方式的例子首先,我们定义一个抽象策略类AttackStrategy

竞赛选题 基于机器视觉的手势检测和识别算法

0前言🔥优质竞赛项目系列,今天要分享的是基于深度学习的手势检测与识别算法该项目较为新颖,适合作为竞赛课题方向,学长非常推荐!🧿更多资料,项目分享:https://gitee.com/dancheng-senior/postgraduate1实现效果废话不多说,先看看学长实现的效果吧2技术原理2.1手部检测主流的手势

Linux内核源码分析 (B.1)深入理解 Linux 虚拟内存管理

Linux内核源码分析(B.1)深入理解Linux虚拟内存管理文章目录Linux内核源码分析(B.1)深入理解Linux虚拟内存管理写在本文开始之前....1.到底什么是虚拟内存地址2.为什么要使用虚拟地址访问内存3.进程虚拟内存空间4\.Linux进程虚拟内存空间4.132位机器上进程虚拟内存空间分布4.264位机器

二刷力扣--二叉树(2)

226.翻转二叉树给你一棵二叉树的根节点root,翻转这棵二叉树,并返回其根节点。使用递归解决。确定函数参数和返回值函数参数为当前节点cur。无返回值。defdd(cur):确定终止条件。当前节点为空则终止。ifnotcur:return单层逻辑反转当前节点的左右,然后递归调用cur.left,cur.rightdef

企业中 Docker 的 Swarm 使用及作用详解

企业中Docker的Swarm使用及作用详解本文将详细介绍企业中Docker的Swarm使用及其在企业中的作用。通过使用Java代码示例,我们将演示Swarm的基本概念、创建Swarm集群以及部署和管理服务等操作。Docker的Swarm功能可帮助企业实现容器编排和集群管理,提供高可用性、可伸缩性和负载均衡等特性。在当

【数据结构】结构实现:顺序存储模式实现堆的相关操作

🚩纸上得来终觉浅,绝知此事要躬行。🌟主页:June-Frost🚀专栏:数据结构🔥该文章着重讲解了使用顺序结构实现堆的插入和删除等操作。目录:🌍二叉树的顺序结构🔭堆🌏代码实现✉️堆的插入✉️堆的删除✉️其他部分❤️结语🌍二叉树的顺序结构二叉树的顺序存储是指将二叉树中的所有节点按照一定的顺序(一层一层)存储

作为公司测开岗的面试官,我是怎么选人的....

最近一段时间面试了不少人,主要是一些测试开发岗,中高级的初级的也都有;也有一些偏业务测试岗的候选人。总结出了一些方法论,或者说更多的是个人作为面试官所遵守的一套面试准则。1.什么是面试?面试不仅仅是你问我答,更多的是一场信息交流,也是一个双方学习的过程。你作为面试官,应聘者的简历要仔细对待,这也是对人家的尊重。从TA的

怒刷LeetCode的第4天(Java版)

#【中秋征文】程序人生,中秋共享#目录第一题题目来源题目内容解决方法方法一:遍历字符串方法二:有限状态机(FiniteStateMachine)方法三:正则表达式第二题题目来源题目内容解决方法方法一:反转数字比较第三题题目来源题目内容解决方法方法一:动态规划方法二:递归回溯第一题题目来源8.字符串转换整数(atoi)-

云计算的未来:云原生架构和自动化运维的崭露头角

文章目录云原生架构:重新定义应用开发和部署什么是云原生架构?为什么云原生架构重要?1.弹性和伸缩性2.故障隔离3.更快的交付4.资源利用率5.多云支持云原生架构的实践步骤1:容器化步骤2:微服务步骤3:自动化运维自动化运维:云计算的关键驱动力什么是自动化运维?为什么自动化运维重要?1.复杂性管理2.资源利用率3.错误率

【汇编】微处理器

【汇编】微处理器文章目录【汇编】微处理器1、微处理器概念1.1关键词1.2分类2、微处理器结构2.1寄存器2.2寄存器&汇编助记符2.3寄存器组成结构3、地址空间3.1存储空间3.1.1虚拟空间(编程空间)3.1.2线性空间3.2I/O空间4、工作模式4.1实地址模式4.2保护虚拟地址模式4.3虚拟86模式1、微处理器

中秋特辑:Java事件监听实现一个猜灯谜小游戏

众所周知,JavaSwing是Java中关于窗口开发的一个工具包,可以开发一些窗口程序,然后由于工具包的一些限制,导致Java在窗口开发商并没有太多优势(当然也有一些第三方的工具包也很好用),不过,在JavaSwing中关于事件的监听机制是我们需要重点掌握的内容。何为事件监听在Java中,事件监听(EventListe

热文推荐