153

Serializable一个类在 Java中到底意味着什么?或者一般来说,就此而言......

4

11 回答 11

146

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

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

于 2010-08-07T09:40:09.907 回答
49

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

假设您有一个像以下这样的班级人员:

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();
    }
于 2015-08-24T12:56:04.317 回答
42

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

于 2010-08-07T09:39:07.690 回答
20

下面是序列化的详细解释:(我自己的博客)

序列化:

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

序列化需要什么?

在现代架构中,总是需要存储对象状态然后检索它。例如在 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 与相应发送者的类不同,则反序列化将导致 InvalidClassException。可序列化的类可以通过声明一个名为“serialVersionUID”的字段来显式声明自己的serialVersionUID,该字段必须是静态的、最终的和long类型:ANY-ACCESS-MODIFIER static final long serialVersionUID = 42L; 如果一个可序列化的类没有显式声明一个 serialVersionUID,然后序列化运行时将根据类的各个方面计算该类的默认 serialVersionUID 值,如 Java(TM) 对象序列化规范中所述。但是,强烈建议所有可序列化的类都显式声明 serialVersionUID 值,因为默认的 serialVersionUID 计算对类细节高度敏感,这些细节可能因编译器实现而异,因此可能在反序列化期间导致意外的 InvalidClassExceptions。因此,为了保证在不同的 java 编译器实现中具有一致的 serialVersionUID 值,可序列化的类必须声明一个显式的 serialVersionUID 值。还强烈建议显式 serialVersionUID 声明尽可能使用 private 修饰符,

如果您注意到我们使用的另一个关键字是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,因为它没有被写入。

于 2017-04-26T00:11:00.850 回答
12

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

于 2010-08-07T09:47:40.500 回答
6

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

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

于 2010-08-07T09:40:18.970 回答
4

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

用数字清楚地解释。请参阅此处了解更多信息

于 2012-01-13T09:10:27.410 回答
3

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

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

于 2014-06-02T09:41:15.593 回答
2

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

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

于 2015-10-25T18:48:51.157 回答
1

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

于 2010-08-07T11:53:28.997 回答
0

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

点击这里查看更多。

于 2021-02-04T17:00:13.907 回答