Serializable หมายถึงอะไร?


140

คลาสที่อยู่Serializableใน Java หมายความว่าอย่างไร หรือโดยทั่วไปสำหรับเรื่องนั้น ...


10
@skaffman นี่คือสิ่งที่กล่าวสำหรับชั้นเรียนSerializable: Serializability 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

32
คำอธิบายที่ดีหากคุณรู้แล้วว่าค่าเฉลี่ยอนุกรมและ deserialized คืออะไร (ไม่ใช่คำชม) คำจำกัดความดังกล่าวช่วยให้คุณเข้าใจปัญหาได้ดีขึ้นในทางเทคนิคเพียงครั้งเดียวและเพียงครั้งเดียวคุณก็มีความรู้แล้ว
Xonatron

คำตอบ:


136

Serializationคือการคงออบเจ็กต์จากหน่วยความจำไปยังลำดับของบิตตัวอย่างเช่นการบันทึกลงในดิสก์ Deserialization ตรงกันข้าม - การอ่านข้อมูลจากดิสก์เพื่อไฮเดรต / สร้างวัตถุ

ในบริบทของคำถามของคุณเป็นอินเทอร์เฟซที่หากนำไปใช้ในคลาสคลาสนี้สามารถต่ออนุกรมและแยกส่วนโดยอัตโนมัติโดย serializers ที่แตกต่างกัน


2
โปรดทราบว่าช่องทั้งหมดที่ไม่ได้ทำเครื่องหมายอย่างชัดเจนมิฉะนั้นจะถูกทำให้เป็นอนุกรมด้วย ซึ่งหมายความว่าคุณสามารถบันทึกโครงสร้างข้อมูลที่ซับซ้อนได้อย่างง่ายดายเพียงแค่จัดลำดับวัตถุรูท
Thorbjørn Ravn Andersen

1
ดังนั้นเมื่อเราพูดถึง "Objects" เราหมายถึงวัตถุที่สร้างอินสแตนซ์โดยคลาสหรือเพียง "Software Objects" เช่นแอสเซมบลีไฟล์ ฯลฯ ? และถ้าเป็นอย่างหลังมันเป็นเพียงวิธีมาตรฐานในการส่งข้อมูลระหว่างโปรแกรมและสภาพแวดล้อมหรือไม่?
Sunburst275

1
@ Sunburst275 - ในกรณีนี้นี่คือการแสดงในหน่วยความจำของคลาสในหน่วยความจำ - เช่นอินสแตนซ์ของคลาส (ไม่มีประเด็นที่แท้จริงในการพูดถึงการทำให้เป็นอนุกรมของแอสเซมบลีเนื่องจากมักอยู่ในดิสก์เป็นไฟล์ที่สามารถ เพียงแค่ส่งไปตามที่เป็นอยู่)
Oded

44

แม้ว่าผู้ใช้ส่วนใหญ่จะให้คำตอบไปแล้ว แต่ฉันอยากจะเพิ่มตัวอย่างสำหรับผู้ที่ต้องการเพื่ออธิบายแนวคิด:

สมมติว่าคุณมีบุคคลในชั้นเรียนดังต่อไปนี้:

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

จากนั้น:

Deserialize จากไฟล์:

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

41

หมายความว่าอินสแตนซ์ของคลาสสามารถเปลี่ยนเป็นไบต์สตรีม (ตัวอย่างเช่นเพื่อบันทึกลงไฟล์) จากนั้นแปลงกลับเป็นคลาสอีกครั้ง การโหลดซ้ำนี้อาจเกิดขึ้นในอินสแตนซ์อื่นของโปรแกรมหรือแม้กระทั่งในเครื่องอื่น การทำให้เป็นอนุกรม (ในภาษาใดก็ได้) เกี่ยวข้องกับปัญหาทุกประเภทโดยเฉพาะอย่างยิ่งเมื่อคุณได้รับการอ้างอิงไปยังวัตถุอื่น ๆ ในซีเรียลที่สามารถใช้งานได้


16

นี่คือคำอธิบายโดยละเอียดของ Serialization : (บล็อกของฉันเอง)

การทำให้เป็นอนุกรม:

การทำให้เป็นอนุกรมเป็นกระบวนการในการทำให้เป็นอนุกรมสถานะของวัตถุจะถูกแสดงและจัดเก็บในรูปแบบของลำดับไบต์ สิ่งนี้สามารถเก็บไว้ในไฟล์ กระบวนการอ่านสถานะของอ็อบเจ็กต์จากไฟล์และเรียกคืนเรียกว่า deserialization

ความต้องการของ Serialization คืออะไร?

ในสถาปัตยกรรมสมัยใหม่มีความจำเป็นที่จะต้องจัดเก็บสถานะของวัตถุแล้วเรียกคืนเสมอ ตัวอย่างเช่นใน Hibernate ในการจัดเก็บวัตถุเราควรทำให้คลาส Serial ได้ สิ่งที่ทำก็คือเมื่อสถานะอ็อบเจ็กต์ถูกบันทึกในรูปแบบของไบต์มันสามารถโอนไปยังระบบอื่นซึ่งสามารถอ่านจากสถานะและดึงคลาสได้ สถานะออบเจ็กต์อาจมาจากฐานข้อมูลหรือ jvm อื่นหรือจากคอมโพเนนต์แยกต่างหาก ด้วยความช่วยเหลือของ Serialization เราสามารถดึงสถานะ Object ได้

ตัวอย่างโค้ดและคำอธิบาย:

ก่อนอื่นมาดู 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;
        }
}

ในโค้ดด้านบนจะเห็นได้ว่าคลาสไอเท็มใช้Serialได้

นี่คืออินเทอร์เฟซที่ช่วยให้คลาสสามารถต่ออนุกรมกันได้

ตอนนี้เราสามารถเห็นตัวแปรที่เรียกว่าserialVersionUIDเริ่มต้นเป็นตัวแปร Long ตัวเลขนี้คำนวณโดยคอมไพลเลอร์ตามสถานะของคลาสและแอ็ตทริบิวต์คลาส นี่คือตัวเลขที่จะช่วยให้ jvm ระบุสถานะของวัตถุเมื่อมันอ่านสถานะของวัตถุจากไฟล์

สำหรับสิ่งนั้นเราสามารถดูเอกสาร Oracle อย่างเป็นทางการ:

รันไทม์การทำให้เป็นอนุกรมเชื่อมโยงกับแต่ละคลาสที่ทำให้เป็นหมายเลขเวอร์ชันเรียกว่า serialVersionUID ซึ่งใช้ในระหว่างการดีซีเรียลไลเซชันเพื่อตรวจสอบว่าผู้ส่งและผู้รับของอ็อบเจ็กต์ที่ทำให้เป็นอนุกรมได้โหลดคลาสสำหรับอ็อบเจ็กต์นั้นที่เข้ากันได้กับการทำให้เป็นอนุกรม ถ้าผู้รับโหลดคลาสสำหรับอ็อบเจ็กต์ที่มี serialVersionUID แตกต่างจากคลาสของผู้ส่งที่เกี่ยวข้องการ deserialization จะทำให้ InvalidClassException คลาสที่ทำให้เป็นอนุกรมสามารถประกาศ serialVersionUID ของตัวเองได้อย่างชัดเจนโดยการประกาศฟิลด์ชื่อ "serialVersionUID" ที่ต้องเป็นแบบคงที่สุดท้ายและประเภทยาว: ANY-ACCESS-MODIFIER static final long serialVersionUID = 42L; ถ้าคลาสที่ทำให้เป็นอนุกรมไม่ได้ประกาศ serialVersionUID อย่างชัดเจน จากนั้นรันไทม์การทำให้เป็นอนุกรมจะคำนวณค่า serialVersionUID ดีฟอลต์สำหรับคลาสนั้นตามลักษณะต่างๆของคลาสตามที่อธิบายไว้ใน Java (TM) Object Serialization Specification อย่างไรก็ตามขอแนะนำอย่างยิ่งว่าคลาสที่ทำให้เป็นอนุกรมได้ทั้งหมดต้องประกาศค่า serialVersionUID อย่างชัดเจนเนื่องจากการคำนวณ serialVersionUID เริ่มต้นมีความอ่อนไหวอย่างมากต่อรายละเอียดของคลาสที่อาจแตกต่างกันไปขึ้นอยู่กับการใช้งานคอมไพลเลอร์และอาจส่งผลให้ InvalidClassExceptions ที่ไม่คาดคิดในระหว่างการ deserialization ดังนั้นเพื่อรับประกันค่า serialVersionUID ที่สอดคล้องกันในการใช้งานคอมไพเลอร์ java ที่แตกต่างกันคลาสที่ทำให้เป็นอนุกรมได้ต้องประกาศค่า serialVersionUID อย่างชัดเจน ขอแนะนำอย่างยิ่งว่าการประกาศ serialVersionUID อย่างชัดเจนให้ใช้ตัวปรับแต่งส่วนตัวหากเป็นไปได้

ถ้าคุณได้สังเกตเห็นมีคำหลักอื่นที่เราได้ใช้ซึ่งเป็นชั่วคราว

หากฟิลด์ไม่สามารถทำให้เป็นอนุกรมได้จะต้องทำเครื่องหมายชั่วคราว ที่นี่เราทำเครื่องหมาย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 เพื่อเขียนอ็อบเจกต์ในไฟล์

สำหรับ Deserializing เราได้ใช้ 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]

สังเกตว่าitemCostPriceจาก deserialized object เป็นโมฆะเนื่องจากไม่ได้เขียน


2
ขอบคุณสำหรับคำอธิบายโดยละเอียดนี้
Promise Preston

นี่เป็นคำอธิบายที่ดี! ขอบคุณ! แต่บอกตามตรงว่ารายการนี้ดูสะอาดตากว่าในบล็อกของคุณมาก อย่างไรก็ตามสิ่งนี้ช่วยได้มาก!
Sunburst275

11

การทำให้เป็นอนุกรมเกี่ยวข้องกับการบันทึกสถานะปัจจุบันของอ็อบเจ็กต์ไปยังสตรีมและการกู้คืนอ็อบเจ็กต์ที่เทียบเท่าจากสตรีมนั้น สตรีมทำหน้าที่เป็นคอนเทนเนอร์สำหรับอ็อบเจ็กต์


1
คำจำกัดความนี้ดูแม่นยำมากขึ้น ขอบคุณ.
Promise Preston

6

Serializable ถูกเรียกในเหมือนอินเทอร์เฟซ แต่คล้ายกับแฟล็กของระบบย่อย Serialization ที่รันไทม์ มันบอกว่าวัตถุนี้สามารถบันทึกได้ ตัวแปรอินสแตนซ์อ็อบเจ็กต์ทั้งหมดยกเว้นอ็อบเจ็กต์ที่ต่ออนุกรมได้และอ็อบเจ็กต์ที่ทำเครื่องหมายระเหยจะถูกบันทึก

ลองนึกภาพแอปพลิเคชันของคุณสามารถเปลี่ยนสีเป็นตัวเลือกได้โดยไม่ต้องเก็บการตั้งค่านั้นไว้ภายนอกคุณจะต้องเปลี่ยนสีทุกครั้งที่ใช้งาน


5
ไม่ใช่ 'แฟล็กไปยังคอมไพเลอร์' เป็นแฟล็กสำหรับระบบย่อย Serialization ที่รันไทม์
Marquis of Lorne

1
@EJP - ขอบคุณไม่รู้ว่า
AphexMunky

ด้วยความเคารพทำไมต้องเขียนในเมื่อคุณไม่รู้ว่ามันเป็นเรื่องจริง? คุณได้ละทิ้ง 'ชั่วคราว' โดยรวมแล้วเป็นคำตอบที่ไม่ดีขออภัย
Marquis of Lorne

19
ถ้าฉันไม่ได้เขียนมันฉันจะไม่ได้รับการแก้ไขและจะแย่ไปกว่านั้น คำตอบอื่น ๆ ทั้งหมดได้ทิ้งไว้ชั่วคราวเช่นกัน คุณไม่ได้เขียนคำตอบคุณแค่หลอกคนอื่น
AphexMunky

4

Serialization เป็นเทคนิคในการจัดเก็บหรือเขียนวัตถุและข้อมูลลงในไฟล์ โดยใช้ObjectOutputStreamและFileOutputStreamคลาส. คลาสเหล่านี้มีวิธีการเฉพาะในการคงอยู่ของวัตถุ ชอบwriteObject();

เพื่อการอธิบายที่ชัดเจนด้วยตัวเลข ดูที่นี่สำหรับข้อมูลเพิ่มเติม


2

เพื่อนำเสนอจากมุมมองอื่น Serialization คืออินเทอร์เฟซชนิดหนึ่งที่เรียกว่า 'marker interface' อินเทอร์เฟซ marker เป็นอินเทอร์เฟซที่ไม่มีการประกาศเมธอด แต่เพียงกำหนด (หรือ "เครื่องหมาย") คลาสที่ใช้อินเทอร์เฟซว่ามีคุณสมบัติบางอย่าง หากคุณเข้าใจความแตกต่างหลากหลายสิ่งนี้จะเข้าท่ามาก ในกรณีของอินเทอร์เฟซตัวทำเครื่องหมายที่ต่ออนุกรมได้เมธอด ObjectOutputStream.write (Object) จะล้มเหลวหากอาร์กิวเมนต์ไม่ได้ใช้อินเทอร์เฟซ นี่เป็นข้อผิดพลาดที่อาจเกิดขึ้นใน java ซึ่งอาจเป็น ObjectOutputStream.write (ต่อเนื่องได้)

แนะนำเป็นอย่างยิ่ง: อ่าน Item 37 จากEffective Java โดย Joshua Blochเพื่อเรียนรู้เพิ่มเติม


2

Serialization:การเขียนสถานะของวัตถุไปยังไฟล์ / เครือข่ายหรือที่ใดก็ได้ (หมายถึงรูปแบบที่รองรับ Java Object เป็น File Supported Form หรือ Network Supported Form)

Deserialization: การอ่านสถานะของวัตถุจากไฟล์ / เครือข่ายหรือที่ใดก็ได้ (หมายถึงไฟล์ / เครือข่ายที่รองรับรูปแบบกับ Java Object Supported Form)


1

เพียงเพื่อเพิ่มคำตอบอื่น ๆ และเกี่ยวกับลักษณะทั่วไป การทำให้เป็นอนุกรมบางครั้งเรียกว่าการเก็บถาวรเช่นใน Objective-C

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.