什么是对象序列化?


Answers:


400

序列化是将对象转换为一系列字节,以便可以将对象轻松保存到持久性存储或跨通信链接进行流传输。然后可以将字节流反序列化-转换为原始对象的副本。


16
这是强制性的吗?发送之前必须先序列化数据吗?转换成什么格式?
弗朗西斯科·科拉莱斯·莫拉莱斯2014年

15
@FranciscoCorralesMorales-在后台,所有数据将在通过流发送之前被序列化。您需要做什么以及将采用哪种格式都取决于您使用的平台和库。
TarkaDaal 2014年

3
@FranciscoCorralesMorales你怎么说?我的意思是您说的格式取决于平台和库。我真的很想知道格式。
JAVA

1
它仅适用于对象吗?我们可以序列化变量(在不使用对象的情况下声明)吗?
Rumado '19

仅@Rumado对象
anKotliner

395

您可以将序列化视为将对象实例转换为字节序列(取决于实现的二进制或非二进制)的过程。

当您想通过网络传输一个对象数据(例如从一个JVM传输到另一个JVM)时,这非常有用。

在Java中,平台内置了序列化机制,但是您需要实现Serializable接口才能使对象可序列化。

您还可以通过将属性标记为transient来防止对象中的某些数据被序列化。

最后,您可以覆盖默认机制,并提供自己的机制。这在某些特殊情况下可能是合适的。为此,您可以使用java中的隐藏功能之一

重要的是要注意,要序列化的是对象的“值”或内容,而不是类定义。因此,方法未序列化。

这是一个非常基本的示例,带有注释以方便阅读:

import java.io.*;
import java.util.*;

// This class implements "Serializable" to let the system know
// it's ok to do it. You as programmer are aware of that.
public class SerializationSample implements Serializable {

    // These attributes conform the "value" of the object.

    // These two will be serialized;
    private String aString = "The value of that string";
    private int    someInteger = 0;

    // But this won't since it is marked as transient.
    private transient List<File> unInterestingLongLongList;

    // Main method to test.
    public static void main( String [] args ) throws IOException  { 

        // Create a sample object, that contains the default values.
        SerializationSample instance = new SerializationSample();

        // The "ObjectOutputStream" class has the default 
        // definition to serialize an object.
        ObjectOutputStream oos = new ObjectOutputStream( 
                               // By using "FileOutputStream" we will 
                               // Write it to a File in the file system
                               // It could have been a Socket to another 
                               // machine, a database, an in memory array, etc.
                               new FileOutputStream(new File("o.ser")));

        // do the magic  
        oos.writeObject( instance );
        // close the writing.
        oos.close();
    }
}

当我们运行该程序时,将创建文件“ o.ser”,我们可以看到后面发生了什么。

如果我们将someInteger的值更改为例如Integer.MAX_VALUE,则可以比较输出以了解差异。

这是一个截屏,显示了这种区别:

替代文字

您能发现差异吗?;)

Java序列化中还有一个其他相关字段:serialversionUID,但我想这已经太长了,无法涵盖它。


1
@ raam86 实例是要序列化的对象。您可能会认为main方法是一个单独的程序,该程序创建类型的对象SerializationSample
OscarRyz 2012年

2
@ raam86是main方法中的第一条语句:SerializationSample instance = new SerializationSample();然后创建输出,并将对象写入该输出。
OscarRyz 2012年

1
哦。没有跟随足够近。大!!
raam86

1
@jacktrades为什么不尝试。只需复制/粘贴示例,然后看到抛出“ NotSerializableException” :)
OscarRyz 2012年

1
@jacktrades,因为尚未告知计算机允许将该对象序列化:) oos是什么意思?
克里斯·贝内特

101

敢于回答这个已有6年历史的问题,对Java初学者来说只是一个非常高级的了解

什么是序列化?

将对象转换为字节

什么是反序列化?

将字节转换回对象(反序列化)。

什么时候使用序列化?

当我们想要持久化对象时。当我们希望对象存在于JVM的生存期之后。

真实示例:

ATM:当帐户持有人尝试通过ATM从服务器提取资金时,帐户持有人的信息(例如提款明细)将被序列化并发送到服务器,在此服务器上,该明细会反序列化并用于执行操作。

如何在Java中执行序列化。

  1. 实现java.io.Serializable接口(标记接口,因此没有实现方法)。

  2. 持久化对象:使用java.io.ObjectOutputStream类,它是一个过滤器流,它是较低层字节流的包装器(用于将Object写入文件系统或将扁平化的对象跨网络传输并在另一侧重建)。

    • writeObject(<<instance>>) -写一个对象
    • readObject() -读取序列化的对象

记得:

序列化对象时,将仅保存对象的状态,而不保存对象的类文件或方法。

当序列化一个2字节的对象时,您会看到51字节的序列化文件。

步骤如何对对象进行序列化和反序列化。

答案:它是如何转换为51字节文件的?

  • 首先写入序列化流魔术数据(STREAM_MAGIC =“ AC ED”,STREAM_VERSION = JVM的版本)。
  • 然后,它写出与实例关联的类的元数据(类的长度,类的名称,serialVersionUID)。
  • 然后它递归地写出超类的元数据,直到找到为止java.lang.Object
  • 然后从与实例关联的实际数据开始。
  • 最后,将与实例相关联的对象的数据从元数据开始写入到实际内容。

如果您对有关Java序列化的更深入的信息感兴趣,请检查此链接

编辑:一个更好的阅读链接

这将回答一些常见问题:

  1. 如何不序列化类中的任何字段。
    答:使用瞬态关键字

  2. 当子类被序列化时,父类会被序列化吗?
    回答:否,如果父级未扩展“可序列化接口”,则父级字段不会序列化。

  3. 父级序列化后,子类会序列化吗?
    回答:是的,默认情况下,子类也被序列化。

  4. 如何避免子类被序列化?
    答:覆盖writeObject和readObject方法并抛出NotSerializableException

    b。您也可以在子类中将所有字段标记为瞬态。

  5. 某些系统级类(例如Thread,OutputStream及其子类和Socket)不可序列化。

3
非常感谢您的简洁回答,它非常有帮助!
Nobi

21

序列化将内存中的“活动”对象转换为可以存储在某处(例如,在内存中,磁盘上)的格式,然后将其“反序列化”回活动对象。


14

我喜欢@OscarRyz呈现的方式。尽管在这里我继续讲述最初由@amitgupta编写的序列化故事

即使了解机器人的类结构并序列化了数据,地球科学家还是无法反序列化使机器人工作的数据。

Exception in thread "main" java.io.InvalidClassException:
SerializeMe; local class incompatible: stream classdesc
:

火星的科学家正在等待全部付款。付款完成后,火星的科学家与地球的科学家共享了serialversionUID。地球的科学家将其设置为机器人课程,一切都变好了。


9

序列化意味着在Java中持久化对象。如果要保存对象的状态并想稍后再重建状态(可能在另一个JVM中),则可以使用序列化。

请注意,仅将保存对象的属性。如果要再次使对象复活,则应该具有类文件,因为将仅存储成员变量,而不存储成员函数。

例如:

ObjectInputStream oos = new ObjectInputStream(                                 
                                 new FileInputStream(  new File("o.ser")) ) ;
SerializationSample SS = (SearializationSample) oos.readObject();

Searializable是一个标记接口,用于标记您的类可序列化。标记接口意味着它只是一个空接口,使用该接口将通知JVM此类可以序列化。


9

我自己博客中的两分钱:

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

序列化:

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

序列化有什么需要?

在现代体系结构中,始终需要存储对象状态然后再检索它。例如在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,因为它没有被写入。

在本文的第一部分中,我们已经讨论了Java序列化的基础知识。

现在,让我们深入讨论它及其工作原理。

首先让我们从serialversionuid开始

的serialVersionUID用作Serializable类版本控制。

如果未明确声明serialVersionUID,则JVM将根据Serializable类的各种属性自动为您完成此操作。

Java的计算serialversionuid的算法(在此处阅读更多详细信息)

  1. 类名。
    1. 类修饰符写为32位整数。
    2. 每个接口的名称按名称排序。
    3. 对于按字段名称排序的类的每个字段(私有静态字段和私有瞬时字段除外:字段名称。字段的修饰符以32位整数形式编写。字段的描述符。
    4. 如果存在类初始值设定项,则写出以下内容:方法的名称。
    5. 方法的修饰符java.lang.reflect.Modifier.STATIC,用32位整数表示。
    6. 方法的描述符()V。
    7. 对于每个按方法名称和签名排序的非私有构造函数:方法名称。方法的修饰符,写为32位整数。方法的描述符。
    8. 对于按方法名称和签名排序的每个非私有方法:方法名称。方法的修饰符,写为32位整数。方法的描述符。
    9. SHA-1算法在DataOutputStream生成的字节流上执行,并生成五个32位值sha [0..4]。哈希值由SHA-1消息摘要的第一和第二个32位值组成。如果消息摘要的结果(五个32位字H0 H1 H2 H3 H4)位于五个名为sha的int值的数组中,则哈希值的计算方式如下:
    long hash = ((sha[0] >>> 24) & 0xFF) |
>            ((sha[0] >>> 16) & 0xFF) << 8 |
>            ((sha[0] >>> 8) & 0xFF) << 16 |
>            ((sha[0] >>> 0) & 0xFF) << 24 |
>            ((sha[1] >>> 24) & 0xFF) << 32 |
>            ((sha[1] >>> 16) & 0xFF) << 40 |
>            ((sha[1] >>> 8) & 0xFF) << 48 |
>        ((sha[1] >>> 0) & 0xFF) << 56;

Java的序列化算法

序列化对象的算法如下所述:
1.它写出与实例关联的类的元数据。
2.它递归地写出超类的描述,直到找到java.lang.object为止。
3.一旦完成元数据信息的写入,便从与实例关联的实际数据开始。但是这一次,它从最高级的超类开始。
4.它递归地写入与实例关联的数据,从最小超类到最大派生类。

注意事项:

  1. 类中的静态字段无法序列化。

    public class A implements Serializable{
         String s;
         static String staticString = "I won't be serializable";
    }
  2. 如果serialversionuid在读取类中不同,则将引发InvalidClassException异常。

  3. 如果一个类实现可序列化,则其所有子类也将可序列化。

    public class A implements Serializable {....};
    
    public class B extends A{...} //also Serializable
  4. 如果一个类具有另一个类的引用,则所有引用都必须是可序列化的,否则将不执行序列化过程。在这种情况下,NotSerializableException在运行时引发。

例如:

public class B{
     String s,
     A a; // class A needs to be serializable i.e. it must implement Serializable
}

1
“串行化是序列化对象状态的过程,该过程以字节序列的形式表示和存储”是没有意义的。如果的serialVersionUID值不同,它将抛出一个InvalidClassException而不是一个ClassCastException。不必浪费所有空间来指定serialVersionUID计算。引用的文档过长,但未链接或未正确引用。这里的绒毛太多,错误太多。
罗恩侯爵

“序列化是序列化的过程”仍然没有意义。
罗恩侯爵(Marques of Lorne)

6

序列化是将对象的状态转换为位以便可以将其存储在硬盘驱动器上的过程。反序列化同一对象时,它将在以后保留其状态。它使您可以重新创建对象,而不必手动保存对象的属性。

http://en.wikipedia.org/wiki/序列化


“ ...以便可以将其存储在硬盘驱动器上。” 或通过二进制协议传输。
Jim Anderson's

4

Java 对象序列化

在此处输入图片说明

Serialization是一种将Java对象图转换为字节数组以进行存储(to disk file)或传输(across a network)的机制,然后通过使用反序列化,我们可以还原对象的图。使用引用共享机制可以正确还原对象图。但是在存储之前,请检查输入文件/网络中的serialVersionUID和.class文件serialVersionUID是否相同。如果不是,则抛出一个java.io.InvalidClassException

每个版本化的类必须标识其能够编写流并可以从中读取的原始类版本。例如,版本化类必须声明:

serialVersionUID语法

// ANY-ACCESS-MODIFIER static final long serialVersionUID = (64-bit has)L;
private static final long serialVersionUID = 3487495895819393L;

serialVersionUID对序列化过程至关重要。但是,对于开发人员来说,将其添加到java源文件中是可选的。如果不包括serialVersionUID,则序列化运行时将生成serialVersionUID并将其与类相关联。序列化的对象将包含此serialVersionUID以及其他数据。

–强烈建议所有可序列化的类显式声明一个serialVersionUID,,since the default serialVersionUID computation is highly sensitive to class details that may vary depending on compiler implementations从而可能在反序列化期间导致意外的serialVersionUID冲突,从而导致反序列化失败。

检查可序列化的类

在此处输入图片说明


Java对象只能序列化。如果一个类或其任何超类实现java.io.Serializable接口或其子接口java.io.Externalizable

  • 一个类必须实现java.io.Serializable接口才能成功序列化其对象。Serializable是一个标记接口,用于通知编译器必须添加实现其的类的可序列化行为。Java虚拟机(JVM)负责其自动序列化。

    瞬态关键字: java.io.Serializable interface

    在序列化对象时,如果我们不希望序列化对象的某些数据成员,则可以使用暂态修饰符。瞬态关键字将防止该数据成员被序列化。

    • 声明为瞬态或静态的字段将被序列化过程忽略。

    瞬态挥发性

    +--------------+--------+-------------------------------------+
    |  Flag Name   |  Value | Interpretation                      |
    +--------------+--------+-------------------------------------+
    | ACC_VOLATILE | 0x0040 | Declared volatile; cannot be cached.|
    +--------------+--------+-------------------------------------+
    |ACC_TRANSIENT | 0x0080 | Declared transient; not written or  |
    |              |        | read by a persistent object manager.|
    +--------------+--------+-------------------------------------+
    class Employee implements Serializable {
        private static final long serialVersionUID = 2L;
        static int id;
    
        int eno; 
        String name;
        transient String password; // Using transient keyword means its not going to be Serialized.
    }
  • 实现Externalizable接口使对象可以完全控制对象序列化表单的内容和格式。调用Externalizable接口的方法writeExternal和readExternal来保存和恢复对象状态。当由类实现时,它们可以使用ObjectOutput和ObjectInput的所有方法来写入和读取自己的状态。对象负责处理发生的任何版本控制。

    class Emp implements Externalizable {
        int eno; 
        String name;
        transient String password; // No use of transient, we need to take care of write and read.
    
        @Override
        public void writeExternal(ObjectOutput out) throws IOException {
            out.writeInt(eno);
            out.writeUTF(name);
            //out.writeUTF(password);
        }
        @Override
        public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
            this.eno = in.readInt();
            this.name = in.readUTF();
            //this.password = in.readUTF(); // java.io.EOFException
        }
    }
  • 仅支持java.io.Serializable或java.io.Externalizable接口的对象可以是written to/read from流。每个可序列化对象的类都经过编码,包括类名和类签名,对象的字段和数组的值以及从初始对象引用的任何其他对象的关闭。

文件的可序列化示例

public class SerializationDemo {
    static String fileName = "D:/serializable_file.ser";

    public static void main(String[] args) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException {
        Employee emp = new Employee( );
        Employee.id = 1; // Can not Serialize Class data.
        emp.eno = 77;
        emp.name = "Yash";
        emp.password = "confidential";
        objects_WriteRead(emp, fileName);

        Emp e = new Emp( );
        e.eno = 77;
        e.name = "Yash";
        e.password = "confidential";
        objects_WriteRead_External(e, fileName);

        /*String stubHost = "127.0.0.1";
        Integer anyFreePort = 7777;
        socketRead(anyFreePort); //Thread1
        socketWrite(emp, stubHost, anyFreePort); //Thread2*/

    }
    public static void objects_WriteRead( Employee obj, String serFilename ) throws IOException{
        FileOutputStream fos = new FileOutputStream( new File( serFilename ) );
        ObjectOutputStream objectOut = new ObjectOutputStream( fos );
        objectOut.writeObject( obj );
        objectOut.close();
        fos.close();

        System.out.println("Data Stored in to a file");

        try {
            FileInputStream fis = new FileInputStream( new File( serFilename ) );
            ObjectInputStream ois = new ObjectInputStream( fis );
            Object readObject;
            readObject = ois.readObject();
            String calssName = readObject.getClass().getName();
            System.out.println("Restoring Class Name : "+ calssName); // InvalidClassException

            Employee emp = (Employee) readObject;
            System.out.format("Obj[No:%s, Name:%s, Pass:%s]", emp.eno, emp.name, emp.password);

            ois.close();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
    public static void objects_WriteRead_External( Emp obj, String serFilename ) throws IOException {
        FileOutputStream fos = new FileOutputStream(new File( serFilename ));
        ObjectOutputStream objectOut = new ObjectOutputStream( fos );

        obj.writeExternal( objectOut );
        objectOut.flush();

        fos.close();

        System.out.println("Data Stored in to a file");

        try {
            // create a new instance and read the assign the contents from stream.
            Emp emp = new Emp();

            FileInputStream fis = new FileInputStream(new File( serFilename ));
            ObjectInputStream ois = new ObjectInputStream( fis );

            emp.readExternal(ois);

            System.out.format("Obj[No:%s, Name:%s, Pass:%s]", emp.eno, emp.name, emp.password);

            ois.close();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

网络上的可序列化示例

在同一台计算机上的不同进程中,甚至在通过网络连接的多台计算机中,通过不同的地址空间分布对象的状态,但这些共享的数据和调用方法可以一起工作。

/**
 * Creates a stream socket and connects it to the specified port number on the named host. 
 */
public static void socketWrite(Employee objectToSend, String stubHost, Integer anyFreePort) {
    try { // CLIENT - Stub[marshalling]
        Socket client = new Socket(stubHost, anyFreePort);
        ObjectOutputStream out = new ObjectOutputStream(client.getOutputStream());
        out.writeObject(objectToSend);
        out.flush();
        client.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
}
// Creates a server socket, bound to the specified port. 
public static void socketRead(  Integer anyFreePort ) {
    try { // SERVER - Stub[unmarshalling ]
        ServerSocket serverSocket = new ServerSocket( anyFreePort );
        System.out.println("Server serves on port and waiting for a client to communicate");
            /*System.in.read();
            System.in.read();*/

        Socket socket = serverSocket.accept();
        System.out.println("Client request to communicate on port server accepts it.");

        ObjectInputStream in = new ObjectInputStream(socket.getInputStream());
        Employee objectReceived = (Employee) in.readObject();
        System.out.println("Server Obj : "+ objectReceived.name );

        socket.close();
        serverSocket.close();
    } catch (IOException | ClassNotFoundException e) {
        e.printStackTrace();
    }
}

@看到


1
当您为已经存在多个非常好的答案的已有六年历史的问题添加答案时,您需要做的比拼写错误的杂音更好。
罗恩侯爵

@ejp Downvoting是表达您的负面意见的工具。令人反感和粗鲁的粗鲁行为是不可接受的。
Konstantinos Chertouras

1
@KonstantinosChertouras提供不赞成投票的理由对发帖人很有帮助,而这就是我的理由,无论您是否喜欢。
洛恩侯爵(Marquis of Lorne)

您还需要避免诸如断言序列化具有安全性之类的错误。没有。
洛恩侯爵

@EJP我已经更新了我的帖子,更正了序列化不是出于安全目的,但是它用于通过反序列化机械化使用SUID将对象的状态转换为任何存储,并使用SUID返回对象的原始状态。JVM to JVM
Yash

3

序列化是将对象保存在存储介质(例如文件或内存缓冲区)中或以二进制形式通过网络连接传输对象的过程。序列化的对象独立于JVM,并且可以由任何JVM重新序列化。在这种情况下,“内存中” java对象状态将转换为字节流。用户无法理解这种类型的文件。它是一种特殊类型的对象,即被JVM(Java虚拟机)重用。序列化对象的过程也称为缩小或编组对象。

要序列化的对象必须实现java.io.SerializableInterface。对象的默认序列化机制将写入对象的类,类签名以及所有非瞬态和非静态字段的值。

class ObjectOutputStream extends java.io.OutputStream implements ObjectOutput,

ObjectOutputinterface扩展了该DataOutput接口,并添加了用于序列化对象和将字节写入文件的方法。该ObjectOutputStream扩展java.io.OutputStream并实现ObjectOutput 对接。它将对象,数组和其他值序列化为流。因此,的构造函数 ObjectOutputStream写为:

ObjectOutput ObjOut = new ObjectOutputStream(new FileOutputStream(f));

上面的代码已用于ObjectOutput通过ObjectOutputStream( )构造函数创建类的实例,该构造函数将的实例FileOuputStream作为参数。

ObjectOutput通过实现ObjectOutputStream该类使用该接口。该ObjectOutputStream构造序列化对象。

在Java中反序列化对象

序列化的相反操作称为反序列化,即从一系列字节中提取数据的过程称为反序列化,也称为膨胀或解组。

ObjectInputStream扩展java.io.InputStream并实现ObjectInput 接口。它从输入流中反序列化对象,数组和其他值。因此,的构造函数 ObjectInputStream写为:

ObjectInputStream obj = new ObjectInputStream(new FileInputStream(f));

程序的上述代码创建ObjectInputStream该类的实例,以反序列化ObjectInputStream该类已序列化的文件。上面的代码使用FileInputStream包含指定文件对象的类的实例创建实例,该对象必须反序列化,因为ObjectInputStream()构造函数需要输入流。



2

将文件作为对象返回:http : //www.tutorialspoint.com/java/java_serialization.htm

        import java.io.*;

        public class SerializeDemo
        {
           public static void main(String [] args)
           {
              Employee e = new Employee();
              e.name = "Reyan Ali";
              e.address = "Phokka Kuan, Ambehta Peer";
              e.SSN = 11122333;
              e.number = 101;

              try
              {
                 FileOutputStream fileOut =
                 new FileOutputStream("/tmp/employee.ser");
                 ObjectOutputStream out = new ObjectOutputStream(fileOut);
                 out.writeObject(e);
                 out.close();
                 fileOut.close();
                 System.out.printf("Serialized data is saved in /tmp/employee.ser");
              }catch(IOException i)
              {
                  i.printStackTrace();
              }
           }
        }

    import java.io.*;
    public class DeserializeDemo
    {
       public static void main(String [] args)
       {
          Employee e = null;
          try
          {
             FileInputStream fileIn = new FileInputStream("/tmp/employee.ser");
             ObjectInputStream in = new ObjectInputStream(fileIn);
             e = (Employee) in.readObject();
             in.close();
             fileIn.close();
          }catch(IOException i)
          {
             i.printStackTrace();
             return;
          }catch(ClassNotFoundException c)
          {
             System.out.println("Employee class not found");
             c.printStackTrace();
             return;
          }
          System.out.println("Deserialized Employee...");
          System.out.println("Name: " + e.name);
          System.out.println("Address: " + e.address);
          System.out.println("SSN: " + e.SSN);
          System.out.println("Number: " + e.number);
        }
    }

这不能回答问题的“什么是”或“请解释”部分。
罗恩侯爵

1

| * | 序列化类:将对象转换为字节,然后将字节转换回对象(反序列化)。

class NamCls implements Serializable
{
    int NumVar;
    String NamVar;
}

| =>对象序列化是将对象的状态转换为字节流的过程。

  • |->在您希望对象在JVM的生存期之外存在时实现。
  • |->序列化对象可以存储在数据库中。
  • |->序列化对象不能被人类读取和理解,因此我们可以实现安全性。

| =>对象反序列化是获取对象状态并将其存储到对象(java.lang.Object)中的过程。

  • |->在存储其状态之前,它检查输入文件/网络中的serialVersionUID和.class文件serialVersionUID是否相同。
    &nbsp&nbsp,如果没有抛出java.io.InvalidClassException。

| => Java对象只有其类或其任何超类才可以序列化

  • 实现java.io.Serializable接口或
  • 其子接口java.io.Externalizable。

| =>类中的静态字段无法序列化。

class NamCls implements Serializable
{
    int NumVar;
    static String NamVar = "I won't be serializable";;
}

| =>如果您不想序列化类的变量,请使用瞬态关键字

class NamCls implements Serializable
{
    int NumVar;
    transient String NamVar;
}

| =>如果一个类实现可序列化,则其所有子类也将可序列化。

| =>如果一个类具有另一个类的引用,则所有引用都必须可序列化,否则将不执行序列化过程。在这种情况下,
NotSerializableException将在运行时引发。


0

我将提供一个类比,以帮助巩固对象序列化/反序列化的概念目的/实用性。

我想象在尝试将对象移动通过暴风雨时使用对象序列化/反序列化。对象实际上是被“分解”或序列化为自身的更多模块化版本(在这种情况下为一系列字节),以便有效地被许可通过介质。从计算的意义上讲,我们可以将字节流过暴风雨的路径视为类似于通过网络行进的字节。我们正在变换对象以符合更理想的运输方式或格式。序列化的对象通常将存储在二进制文件中,以后可以从中读取,写入或同时读取和写入。

也许一旦我们的对象能够以分解后的字节序列的形式流失,我们可能希望将对象的表示形式作为二进制数据存储在数据库或硬盘驱动器中。不过,主要的收获是,通过序列化/反序列化,我们可以选择让对象在序列化后保持其二进制形式,或者通过执行反序列化“检索”对象的原始形式。

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.