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.