Java中对象序列化的必要性是什么?

74

有人能告诉我Java对象序列化的必要性,并举例说明需要的情况吗?(我已经了解什么是序列化,我只想了解何时使用它以及如何使用它)。


3
在序列化中,重点不是将对象转换为位和字节,因为对象本身已经是位和字节。序列化是使对象的“状态”持久化的过程。请阅读此内容:https://dev59.com/DGct5IYBdhLWcg3wI6J6#69416269 - aderchox
9个回答

206
关于序列化的短篇故事
经过多年的辛勤工作,地球的科学家们开发出了一款可以帮助他们日常工作的机器人。但是这款机器人的功能比火星科学家们开发的机器人要少。
在两个星球的科学家们的会议后,决定火星将把他们的机器人送到地球上。但是出现了一个问题。将100台机器人送到地球的成本是1亿美元。而且需要大约60天的旅行时间。
最终,火星的科学家们决定与地球的科学家们分享他们的秘密。这个秘密是关于类/机器人的结构。地球的科学家们在地球上开发了相同的结构。火星的科学家们对每个机器人的数据进行了序列化,并将其发送到地球。地球的科学家们对数据进行了反序列化,并相应地输入到每个机器人中。
这个过程节省了他们在传输大量数据时的时间。
其中一些机器人在火星上被用于一些防御工作。因此,在将数据发送到地球之前,他们的科学家们将这些机器人的一些关键属性标记为瞬态。请注意,在对象反序列化时,瞬态属性被设置为null(引用类型)或默认值(原始类型)。
地球科学家注意到的另一个问题是,火星科学家要求他们创建一些静态变量来保存有关环境的详细信息。这些详细信息被一些机器人使用。但是火星科学家不分享这些细节,因为地球的环境与火星的环境不同。
尽管了解了机器人类的结构并具有序列化数据,地球科学家仍然无法对数据进行反序列化,以使机器人正常工作。
Exception in thread "main" java.io.InvalidClassException:
SerializeMe; local class incompatible: stream classdesc
:

火星的科学家们正在等待完整付款。一旦付款完成,火星的科学家们将serialversionUID与地球的科学家们共享。地球的科学家将其设置为机器人类,然后一切开始运作。

更新

尽管借助序列化的帮助,他们能够使用信号而不是实际的宇宙飞船发送数据,但他们意识到发送大量数据仍然是一个挑战。序列化使过程更加便宜和快速,但仍然很慢。因此,不同的科学家提出了不同的想法来减小数据大小。一些科学家建议压缩数据,一些科学家建议使用不同的机制来表示数据,以便可以进行反序列化。其中一些想法包括XMLJSONmsgpackNimn


6
我在这里续写了你的故事(链接地址:https://dev59.com/xHRB5IYBdhLWcg3w-8A9#6322836)。 - noquery
4
我很喜欢这个故事。希望每个人都能像这样用故事来解释。我如何进行多次点赞? - Khatri
2
谢谢。我喜欢在任何可以的地方用自然的方式解释事情。 - Amit Kumar Gupta
1
JSON或XML如何解决“缓慢”的问题?序列化和JSON/XML之间的关系是什么? - yuxh
2
这是一种非常有趣的解释方式。这个故事让我想起了《星际迷航》电视系列。 - mss
显示剩余6条评论

155

当需要发送数据或将其存储在文件中时,通常会使用序列化。这里的数据指的是对象而不是文本。

现在的问题是,网络基础设施和硬盘都是只理解位和字节而不理解JAVA对象的硬件组件。

序列化是将Java对象的值/状态转换为字节以便于发送或存储的过程。

这类比于你的语音如何通过PSTN电话线传输。


通过有线传输语音的模拟电话部分非常好。谢谢。 - fuyi

45
Java序列化(特别是SerializableExernalizable接口)允许您自动或手动地从磁盘或网络读取/写入任意复杂的Java对象。而XML和JSON是文本格式,Java序列化是二进制格式。(序列化也是一个简单的读/写数据的概念,但由于问题涉及Java,我假设您指的是内置的序列化系统,即Serializable/Exernalizable) "实现Serializable"相对于XML/JSON的优点
首先,您可以基本上免费获得序列化。您不需要对对象进行很多更改,以便让序列化机制与之配合工作。另一个优点是,因为它是二进制格式,所以比文本格式更紧凑,因此可能使用更少的空间(这对于节省网络带宽或节省磁盘存储空间非常有用)。 "实现Serializable"相对于XML/JSON的缺点
Java内置序列化的缺点是,如果您对对象进行更改,则使不同的序列化格式兼容可能会成为一个真正的噩梦。此外,虽然您可以手动编辑XML和JSON,但无法编辑序列化的Java对象(除非将其读入Java)。出于同样的原因,调试XML和JSON比二进制格式更容易,因为XML和JSON是人类可读的。Java内置序列化机制的另一个缺点是,您不能(轻松地)将数据从另一种编程语言序列化/反序列化。 读写数据的替代技术
除了Java内置的序列化之外,还有其他序列化技术可以让您兼顾紧凑的二进制格式、语言互操作性、易于版本兼容性,并且通常还提供调试工具,使得轻松地将二进制数据转储为可读格式。例如,Google的开源protocol buffersMessagePack都是序列化库/格式的例子,可以让您读/写紧凑的二进制数据并轻松维护版本兼容性。与Java内置序列化相比,这些库最大的缺点是它们涉及纯老数据对象进行序列化(而不是还具有相关行为的更全面的Java对象);然而,事实上,这种劣势实际上是一种优势,因为将存储信息的数据模型与其包装或派生自的对象分开是一种良好的编程实践,并且使得支持多个格式变得更加容易。

用法
既然你询问的是需求,而不仅仅是定义,那么就有很多用例:

  1. 简单地保存数据以供以后使用。例如,假设你正在编写一个视频游戏。你的程序不会永远运行;即使它从未崩溃(希望如此),用户也可能在某个时候退出程序,或者操作系统可能杀死程序以节省资源(例如,在Android上,用户没有交互的后台进程经常被操作系统故意杀死以回收系统资源,如RAM)。为了确保用户不从头开始,并且可以从之前的存档点或最近的存档点继续游戏,您需要将游戏状态写入持久存储(即硬盘、用户的Google Drive帐户等)。为此,您需要将表示游戏状态的内存中的数据结构转换为原始字节,然后写入磁盘(或任何其他系统)。

  2. 从远程服务器检索信息。让我们继续以游戏为例...假设您正在创建一个在线多人游戏,或者您想要使提供新级别或新物品变得可能,而无需用户更新其应用程序。为此,您希望有关在线玩家的信息或有关新级别/物品的信息从服务器计算机(您将其用作各种设备上安装的应用程序的所有副本的联系点)通信到应用程序的各个副本。服务器和应用程序都需要这些数据结构的某种内存表示形式(例如,其他玩家的位置、新级别的结构、新物品的描述/图像等),但是为了将信息从服务器传输到设备上的应用程序,通信系统由原始字节组成,因此需要一种方法将数据转换为原始字节,并从原始字节转换回有意义的内存数据结构。

几乎所有不同进程/应用程序之间或应用程序与某些存储系统之间的通信都需要一种序列化机制。

17

当您想将对象的状态保存到文件或通过网络发送时,需要将它转换为一系列字节。这就是所谓的序列化。

Java内置了这种机制,其他选项包括XML或JSON。

需要进行序列化的示例:缓存对象、进行远程方法调用、将对象图保存到磁盘。


4
  • 如果您想将一个对象(结构)存储在磁盘上,您需要进行序列化。
  • 在传输之前,Web服务需要将对象序列化为XML。

2

您还可以使用序列化实现对象克隆


2

序列化通常指将对象转换为一系列位。在Java中,它是一个必不可少的部分,因为Java主要用于基于Web的应用程序,即使要使数据在网络上可用。


0

当一个类需要将数据发送到另一个类时,如果这两个类位于不同的位置或硬盘上,就需要进行序列化。

反序列化是序列化的逆操作。

String类和所有包装类默认实现了Serializable接口。

Serializable接口也是一个标记接口,它为您的类提供了序列化的能力。因此,如果我们想要通过网络发送对象的状态,就应该实现Serializable接口。


0
public class Serializer {

    public static void write(Object o, File f) throws IOException {
        f.delete();
        f.createNewFile();
        FileOutputStream fileOut = new FileOutputStream(f);
        ObjectOutputStream out = new ObjectOutputStream(fileOut);
        out.writeObject(o);
        out.close();
        fileOut.close();
    }

    public static Object read(File f) throws Exception{
        FileInputStream fileIn = new FileInputStream(f);
        ObjectInputStream in = new ObjectInputStream(fileIn);
        Object e = in.readObject();
        in.close();
        fileIn.close();
        return e;
    }

    public static byte[] toBytes(Object o) {
        byte[] bytes = null;
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutput out = null;
        try {
            out = new ObjectOutputStream(bos);
            out.writeObject(o);
            out.flush();
            bytes = bos.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                bos.close();
            } catch (IOException ex) {
                // ignore close exception
            }
        }

        return bytes;
    }

    public static Object fromBytes(byte[] bytes) {
        Object o = null;
        ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
        ObjectInput in = null;
        try {
            in = new ObjectInputStream(bis);
            o = in.readObject();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } finally {
            try {
                if (in != null) {
                    in.close();
                }
            } catch (IOException ex) {
                // ignore close exception
            }
        }

        return o;
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接