可序列化是啥意思?

Posted

技术标签:

【中文标题】可序列化是啥意思?【英文标题】:What does Serializable mean?可序列化是什么意思? 【发布时间】:2011-03-26 16:30:05 【问题描述】:

在 Java 中,类为 Serializable 究竟意味着什么?或者一般来说,就此而言......

【问题讨论】:

@skaffman 这就是它对班级的说法SerializableSerializability of a class is enabled by the class implementing the java.io.Serializable interface. Classes that do not implement this interface will not have any of their state serialized or deserialized. All subtypes of a serializable class are themselves serializable. The serialization interface has no methods or fields and serves only to identify the semantics of being serializable. 如果您已经知道序列化和反序列化的含义,这是一个很好的解释。 (不是恭维。)这样的定义可以帮助您在技术上更好地理解问题一次,而且只有一次,您已经对此有所了解。 @RitwikBose 所以,java.io.Serializable 是一个标签接口 【参考方案1】:

Serialization 将一个对象从内存持久化到一个位序列中,例如保存到磁盘上。反序列化是相反的——从磁盘读取数据来合成/创建一个对象。

在你的问题的上下文中,它是一个接口,如果在一个类中实现,这个类可以被不同的序列化器自动序列化和反序列化。

【讨论】:

另请注意,所有未明确标记的字段也将被序列化。这意味着您只需序列化根对象即可轻松保存复杂的数据结构。 所以当我们谈论“对象”时,我们是指由类实例化的对象,还是只是任何“软件对象”,如程序集、文件等?如果是后者,它只是一种在程序和环境之间发送数据的标准化方式吗? @Sunburst275 - 在这种情况下,这是内存中类的内存表示 - 即类的实例(谈论程序集的序列化没有真正意义,因为它们通常作为文件在磁盘上,可以简单地按原样发送)。【参考方案2】:

虽然大部分用户已经给出了答案,但我想为需要它的人添加一个示例以解释这个想法:

假设你有一个像下面这样的班级人:

public class Person implements java.io.Serializable 
    /**
     * 
     */
    private static final long serialVersionUID = 1L;
    public String firstName;
    public String lastName;
    public int age;
    public String address;

    public void play() 
        System.out.println(String.format(
                "If I win, send me the trophy to this address: %s", address));
    
    @Override
    public String toString() 
        return String.format(".....Person......\nFirst Name = %s\nLast Name = %s", firstName, lastName);
    

然后你创建一个像这样的对象:

Person william = new Person();
        william.firstName = "William";
        william.lastName = "Kinaan";
        william.age = 26;
        william.address = "Lisbon, Portugal";

您可以将该对象序列化为多个流。我将对两个流执行此操作:

序列化到标准输出:

public static void serializeToStandardOutput(Person person)
            throws IOException 
        OutputStream outStream = System.out;
        ObjectOutputStream stdObjectOut = new ObjectOutputStream(outStream);
        stdObjectOut.writeObject(person);
        stdObjectOut.close();
        outStream.close();
    

序列化到文件:

public static void serializeToFile(Person person) throws IOException 
        OutputStream outStream = new FileOutputStream("person.ser");
        ObjectOutputStream fileObjectOut = new ObjectOutputStream(outStream);
        fileObjectOut.writeObject(person);
        fileObjectOut.close();
        outStream.close();
    

然后:

从文件反序列化:

public static void deserializeFromFile() throws IOException,
            ClassNotFoundException 
        InputStream inStream = new FileInputStream("person.ser");
        ObjectInputStream fileObjectIn = new ObjectInputStream(inStream);
        Person person = (Person) fileObjectIn.readObject();
        System.out.println(person);
        fileObjectIn.close();
        inStream.close();
    

【讨论】:

谢谢。我想我现在明白了。【参考方案3】:

这意味着可以将类的实例转换为字节流(例如,保存到文件中),然后再次转换回类。这种重新加载可能发生在程序的不同实例中,甚至可能发生在不同的机器上。不过,序列化(任何语言)都涉及各种问题,尤其是当您在可序列化对象中引用了其他对象时。

【讨论】:

【参考方案4】:

Here is a detailed explanation of the Serialization:(我自己的博客)

序列化:

序列化是将对象的状态序列化的过程,以字节序列的形式表示和存储。这可以存储在文件中。从文件中读取对象状态并恢复它的过程称为反序列化。

序列化需要什么?

在现代架构中,总是需要存储对象状态然后检索它。例如在 Hibernate 中,要存储一个对象,我们应该使类 Serializable。它的作用是,一旦对象状态以字节的形式保存,它就可以传输到另一个系统,然后该系统可以从状态中读取并检索类。对象状态可以来自数据库或不同的 jvm 或来自单独的组件。在序列化的帮助下,我们可以检索对象状态。

代码示例及说明:

首先让我们看一下Item Class:

public class Item implements Serializable

    /**
    *  This is the Serializable class
    */
    private static final long serialVersionUID = 475918891428093041L;
    private Long itemId;
    private String itemName;
    private transient Double itemCostPrice;
    public Item(Long itemId, String itemName, Double itemCostPrice) 
        super();
        this.itemId = itemId;
        this.itemName = itemName;
        this.itemCostPrice = itemCostPrice;
      

      public Long getItemId() 
          return itemId;
      

     @Override
      public String toString() 
          return "Item [itemId=" + itemId + ", itemName=" + itemName + ", itemCostPrice=" + itemCostPrice + "]";
       


       public void setItemId(Long itemId) 
           this.itemId = itemId;
       

       public String getItemName() 
           return itemName;
       
       public void setItemName(String itemName) 
            this.itemName = itemName;
        

       public Double getItemCostPrice() 
            return itemCostPrice;
        

        public void setItemCostPrice(Double itemCostPrice) 
             this.itemCostPrice = itemCostPrice;
        

在上面的代码中可以看出Item类实现了Serializable

这是使类可序列化的接口。

现在我们可以看到一个名为 serialVersionUID 的变量被初始化为 Long 变量。这个数字是编译器根据类的状态和类属性计算出来的。这是帮助 jvm 在从文件中读取对象状态时识别对象状态的数字。

为此,我们可以查看官方的 Oracle 文档:

序列化运行时与每个可序列化类关联 版本号,称为serialVersionUID,用于 反序列化以验证序列化的发送者和接收者 对象已加载与该对象兼容的类 关于序列化。如果接收者已经为 对象的 serialVersionUID 与 对应的发件人的类,然后反序列化将导致 无效类异常。可序列化的类可以声明自己的 通过声明一个名为的字段显式 serialVersionUID "serialVersionUID" 必须是静态的、最终的并且是 long 类型: ANY-ACCESS-MODIFIER static final long serialVersionUID = 42L;如果一个 可序列化类没有显式声明一个serialVersionUID, 然后序列化运行时将计算一个默认值 该类的 serialVersionUID 值基于 类,如 Java(TM) 对象序列化中所述 规格。但是,强烈建议所有 可序列化的类显式声明 serialVersionUID 值,因为 默认的 serialVersionUID 计算对类高度敏感 细节可能因编译器实现而异,并且可以 因此导致意外的 InvalidClassExceptions 在 反序列化。因此,要保证一致的serialVersionUID 跨不同 java 编译器实现的值,一个可序列化的 类必须声明一个显式的 serialVersionUID 值。也是 强烈建议显式 serialVersionUID 声明使用 可能的情况下使用私有修饰符,因为此类声明仅适用于 立即声明类--serialVersionUID 字段不是 作为继承成员有用。

如果您注意到我们使用了另一个关键字 transient

如果字段不可序列化,则必须将其标记为瞬态。这里我们将 itemCostPrice 标记为瞬态,并且不希望将其写入文件中

现在让我们看看如何在文件中写入对象的状态,然后从那里读取它。

public class SerializationExample 

    public static void main(String[] args)
        serialize();
       deserialize();
     

    public static void serialize()

         Item item = new Item(1L,"Pen", 12.55);
         System.out.println("Before Serialization" + item);

         FileOutputStream fileOut;
         try 
             fileOut = new FileOutputStream("/tmp/item.ser");
             ObjectOutputStream out = new ObjectOutputStream(fileOut);
             out.writeObject(item);
             out.close();
             fileOut.close();
             System.out.println("Serialized data is saved in /tmp/item.ser");
            catch (FileNotFoundException e) 

                  e.printStackTrace();
            catch (IOException e) 

                  e.printStackTrace();
           
      

    public static void deserialize()
        Item item;

        try 
                FileInputStream fileIn = new FileInputStream("/tmp/item.ser");
                ObjectInputStream in = new ObjectInputStream(fileIn);
                item = (Item) in.readObject();
                System.out.println("Serialized data is read from /tmp/item.ser");
                System.out.println("After Deserialization" + item);
         catch (FileNotFoundException e) 
                e.printStackTrace();
         catch (IOException e) 
               e.printStackTrace();
         catch (ClassNotFoundException e) 
               e.printStackTrace();
        
     

在上面我们可以看到一个对象的序列化和反序列化的例子。

为此,我们使用了两个类。为了序列化我们使用了 ObjectOutputStream 的对象。我们已经使用 writeObject 方法将对象写入文件。

对于反序列化,我们使用了从文件中读取对象的 ObjectInputStream。它使用 readObject 从文件中读取对象数据。

上述代码的输出如下:

Before SerializationItem [itemId=1, itemName=Pen, itemCostPrice=12.55]
Serialized data is saved in /tmp/item.ser
After DeserializationItem [itemId=1, itemName=Pen, itemCostPrice=null]

请注意,来自反序列化对象的 itemCostPricenull,因为它没有被写入。

【讨论】:

这是一个很好的解释!谢谢!但老实说,这篇文章看起来比你博客上的要干净得多。无论如何,这有很大帮助!【参考方案5】:

序列化涉及将对象的当前状态保存到流中,并从该流中恢复等效对象。流充当对象的容器

【讨论】:

这个定义似乎更准确。谢谢。【参考方案6】:

Serializable 在运行时像接口一样被调用,但它更像是序列化子系统的标志。它说这个对象可以保存。除了不可序列化的对象和标记为 volatile 的对象之外的所有 Objects 实例变量都将被保存。

想象一下,您的应用程序可以选择更改颜色,而无需将该设置保持在外部,您每次运行时都需要更改颜色。

【讨论】:

它不是“编译器的标志”。它是运行时序列化子系统的标志。 @EJP - 谢谢,不知道 恕我直言,既然你不知道它是真的,为什么还要写它呢?您还遗漏了“瞬态”。总而言之,一个糟糕的答案,对不起。 如果我没有写它,我就不会被纠正,而且会更糟。所有其他答案也都离开了瞬态。你甚至没有写一个答案,你只是在拖钓别人。【参考方案7】:

序列化是一种将对象和数据存储或写入文件的技术。通过使用 ObjectOutputStreamFileOutputStream 类。这些类有其特定的方法来持久化对象。喜欢writeObject();

为了清楚地解释数字。 See Here for more info

【讨论】:

【参考方案8】:

从另一个角度呈现。序列化是一种称为“标记接口”的接口。标记接口是不包含方法声明的接口,但 仅仅指定(或“标记”)实现接口的类具有 一些财产。如果您了解多态性,这将非常有意义。在 Serializable 标记接口的情况下,如果 ObjectOutputStream.write(Object) 方法的参数未实现该接口,则该方法将失败。这是java中的一个潜在错误,可能是 ObjectOutputStream.write(Serializable)

强烈推荐:阅读 Joshua Bloch 的 Effective Java 中的第 37 条以了解更多信息。

【讨论】:

【参考方案9】:

序列化:将对象的状态写入文件/网络或任何地方。 (意味着 Java 对象支持的形式到文件支持的形式或网络支持的形式)

反序列化:从文件/网络或任何地方读取对象的状态。 (平均文件/网络支持形式到 Java 对象支持形式)

【讨论】:

【参考方案10】:

只是为了补充其他答案和一般性。序列化有时称为归档,例如在 Objective-C 中。

【讨论】:

【参考方案11】:

序列化对象意味着将其状态转换为字节流,以便字节流可以恢复为对象的副本。如果 Java 对象的类或其任何超类实现了 java.io.Serializable 接口或其子接口 java.io.Externalizable,则它是可序列化的。反序列化是将对象的序列化形式转换回对象副本的过程

点击here查看更多。

【讨论】:

以上是关于可序列化是啥意思?的主要内容,如果未能解决你的问题,请参考以下文章

C#序列化和反序列化到底是啥意思?

sfence 文档中的“序列化操作”是啥意思?

(JSON) 序列化和反序列化,这个是啥意思呀?

python turtle 中的坏颜色序列是啥意思?

sas时间序列中mu,ar1,1是啥意思

使自定义 .NET 异常可序列化的正确方法是啥?