可序列化是什么意思?


136

SerializableJava中的类对什么意味着什么?或者总的来说...


10
@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.
Ritwik Bose 2010年

32
如果您已经知道序列化和反序列化的含义,那么这是一个很好的解释。(不是夸奖。)这样的定义可以帮助您从技术上更好地理解问题,而且只有一次,您已经对此有所了解。
Xonatron

Answers:


131

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

就您的问题而言,它是一个接口,如果在一个类中实现,则该类可以由不同的序列化程序自动进行序列化和反序列化。


2
另请注意,所有未明确标记的字段也将被序列化。这意味着您只需序列化根对象即可轻松保存复杂的数据结构。
托尔比约恩Ravn的安徒生

1
因此,当我们谈论“对象”时,是指由类实例化的对象,还是仅指任何“软件对象”(如程序集,文件等)实例化的对象?如果是后者,这是否只是在程序和环境之间发送数据的标准化方法?
Sunburst275

1
@ Sunburst275-在这种情况下,这是内存中类的内存表示形式-即类的实例(谈论程序集的序列化没有真正意义,因为它们通常在磁盘上作为文件可以只需按原样发送)。
奥德

43

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

假设您有一个如下的班级人员:

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();
    }

谢谢。我想我现在明白了。
Sunburst275

39

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


14

这是序列化的详细说明:(我自己的博客)

序列化:

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

序列化有什么需要?

在现代体系结构中,始终需要先存储对象状态然后再检索它。例如在Hibernate中,要存储对象,我们应该使类Serializable。它的作用是,一旦对象状态以字节形式保存,就可以将其转移到另一个系统,该系统可以从状态中读取并检索类。对象状态可以来自数据库或其他jvm,也可以来自单独的组件。借助序列化,我们可以检索对象状态。

代码示例和说明:

首先让我们看一下Item类:

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从文件中读取对象的状态时,该数字将帮助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,因为它没有被写入。


2
感谢您的详细说明。
Promise Preston

这是一个很好的解释!谢谢!但老实说,该条目看上去比您博客中的条目干净得多。无论如何,这很有帮助!
Sunburst275

11

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


1
这个定义似乎更精确。谢谢。
Promise Preston

6

Serializable的调用就像一个接口,但它更像是编译器的标志。它说可以保存该对象。除无可序列化对象和标记为volatile的对象外,所有Objects实例变量都将被保存。

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


5
它不是“编译器的标志”。在运行时,它是序列化子系统的标志。
罗恩侯爵

1
@EJP -谢谢,不知道
AphexMunky

尊敬的,为什么在您不知道它是真的时候写它呢?您还省略了“瞬态”。总体而言,答案很差,对不起。
罗恩侯爵

19
如果我没有写它,我将不会得到纠正,而且情况还会更糟。所有其他答案也都没有瞬态。您甚至都没有写答案,只是在拖拉其他人。
AphexMunky 2010年

4

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

用数字清楚地解释。有关更多信息,请参见此处


2

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

强烈建议:阅读Joshua Bloch撰写的有效Java中的第37项,以了解更多信息。


2

序列化:将对象的状态写入文件/网络或任何地方。(将“ Java对象支持的表单”转换为“文件支持的表单”或“网络支持的表单”)

反序列化:从文件/网络或任何地方读取对象的状态。(将“文件/网络支持的形式”转换为“ Java对象支持的形式”)


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.