当前位置:  开发笔记 > 编程语言 > 正文

什么是对象序列化?

如何解决《什么是对象序列化?》经验,为你挑选了8个好方法。

"对象序列化"是什么意思?你能用一些例子解释一下吗?



1> TarkaDaal..:

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


这是强制性的吗?在发送之前我必须序列化数据吗?它转换为什么格式?
@FranciscoCorralesMorales - 在幕后,所有数据将在通过流发送之前被序列化.您需要做多少以及它将采用何种格式,这取决于您使用的平台和库.
@FranciscoCorralesMorales你是怎么说的?我的意思是你说格式取决于平台和库.我真的想知道格式.

2> OscarRyz..:

您可以将序列化视为将对象实例转换为字节序列的过程(可能是二进制或不依赖于实现).

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

在Java中,序列化机制内置于平台中,但您需要实现Serializable接口以使对象可序列化.

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

最后,您可以覆盖默认机制,并提供您自己的机制; 这可能适用于某些特殊情况.为此,您可以使用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 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,但我想这已经太长了,无法覆盖它.


@ raam86是main方法中的第一个语句:`SerializationSample instance = new SerializationSample();`然后创建输出并将对象写入该输出.

3> VdeX..:

敢于回答6年前的问题,为刚接触Java的人们增加了一个非常高层次的理解

什么是序列化?

将对象转换为字节和字节回到对象(反序列化).

何时使用序列化?

当我们想要坚持对象时.当我们希望对象存在于JVM的生命周期之外时.

真实世界的例子:

ATM:当账户持有人试图通过ATM从服务器提取资金时,账户持有人信息(如撤销详细信息)将被序列化并发送到服务器,其中详细信息被反序列化并用于执行操作.

如何在java中执行序列化.

    实现java.io.Serializable接口(标记接口,因此无法实现).

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

    writeObject(<>) - 写一个对象

    readObject() - 读取序列化对象

记得:

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

序列化2字节对象时,您会看到51字节的序列化文件.

步骤如何序列化和反序列化对象.

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

首先写入序列化流魔术数据(STREAM_MAGIC ="AC ED"和STREAM_VERSION = JVM的版本).

然后它写出与实例关联的类的元数据(类的长度,类的名称,serialVersionUID).

然后它递归地写出超类的元数据,直到它找到java.lang.Object.

然后从与实例关联的实际数据开始.

最后将与实例关联的对象的数据从元数据开始写入实际内容.

如果您对有关Java序列化的部门信息感兴趣,请查看此链接.

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

这将回答几个常见问题:

    如何不在课堂上序列化任何字段.
    Ans:使用transient关键字

    当子类被序列化时,父类是否被序列化?
    Ans:不,如果父级没有扩展Serializable接口,则parent字段不会被序列化.

    当父序列化时,子类是否被序列化?
    Ans:是的,默认情况下,子类也会被序列化.

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

    湾 你也可以在子类中标记瞬态的所有字段.

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


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

4> Kent Boogaar..:

序列化将内存中的"实时"对象转换为可以存储在某处的格式(例如,在内存中,在磁盘上),然后"反序列化"回到活动对象中.



5> noquery..:

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

即使了解机器人类结构并具有序列化数据,地球科学家也无法对可以使机器人工作的数据进行反序列化.

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

火星的科学家们正在等待全部付款.付款完成后,Mars的科学家们与地球科学家分享了serialversionUID.地球科学家将它设置为机器人类,一切都变得很好.



6> Pritam Baner..:

我自己博客的两分钱:

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

连载:

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

序列化需要什么?

在现代架构中,总是需要存储对象状态然后检索它.例如在Hibernate中,要存储对象,我们应该使类Serializable.它的作用是,一旦对象状态以字节的形式保存,它就可以转移到另一个系统,然后该系统可以从状态读取并检索该类.对象状态可以来自数据库或不同的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;
        }
}

在上面的代码中可以看出Item类实现了Serializable.

这是使类可序列化的接口.

现在我们可以看到一个名为serialVersionUID的变量被初始化为Long变量.此编号由编译器根据类的状态和类属性计算.这是一个有助于jvm在从文件中读取对象状态时识别对象状态的数字.

为此,我们可以查看官方Oracle文档:

序列化运行时将每个可序列化类与版本号相关联,称为serialVersionUID,在反序列化期间使用该版本号来验证序列化对象的发送方和接收方是否已加载与该序列化兼容的该对象的类.如果接收者为具有与相应发送者类的serialVersionUID不同的对象加载了一个类,则反序列化将导致InvalidClassException.可序列化的类可以通过声明一个名为"serialVersionUID"的字段来显式声明它自己的serialVersionUID,该字段必须是static,final和long类型:ANY-ACCESS-MODIFIER static final long serialVersionUID = 42L; 如果可序列化类未显式声明serialVersionUID,则序列化运行时将基于类的各个方面计算该类的默认serialVersionUID值,如Java(TM)对象序列化规范中所述.但是,强烈建议所有可序列化类显式声明serialVersionUID值,因为默认的serialVersionUID计算对类细节高度敏感,这些细节可能因编译器实现而异,因此在反序列化期间可能导致意外的InvalidClassExceptions.因此,为了保证跨不同java编译器实现的一致的serialVersionUID值,可序列化类必须声明显式的serialVersionUID值.强烈建议显式serialVersionUID声明尽可能使用private修饰符,因为此类声明仅适用于立即声明的类 - 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方法在文件中写入对象.

对于反序列化,我们使用了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算法(点击此处了解更多详情)

    班级名称.

      类修饰符写为32位整数.

      按名称排序的每个接口的名称.

      对于按字段名称排序的类的每个字段(私有静态和私有瞬态字段除外:字段的名称.字段的修饰符,写为32位整数.字段的描述符.

      如果存在类初始值设定项,请写出以下内容:方法的名称,.

      方法的修饰符java.lang.reflect.Modifier.STATIC,写为32位整数.

      方法的描述符,()V.

      对于按方法名称和签名排序的每个非私有构造函数:方法的名称,.该方法的修饰符写为32位整数.方法的描述符.

      对于按方法名称和签名排序的每个非私有方法:方法的名称.该方法的修饰符写为32位整数.方法的描述符.

      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.它写出与实例关联的类的元数据.
它以递归方式写出超类的描述,直到找到java.lang.object.
3.一旦完成编写元数据信息,它就会从与实例关联的实际数据开始.但这一次,它从最顶层的超类开始.
它以递归方式写入与实例关联的数据,从最小类到最大类.

要记住的事情:

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

    public class A implements Serializable{
         String s;
         static String staticString = "I won't be serializable";
    }
    

    如果readversionuid在读取类中不同,则会引发InvalidClassException异常.

    如果一个类实现了serializable,那么它的所有子类也将是可序列化的.

    public class A implements Serializable {....};
    
    public class B extends A{...} //also Serializable
    

    如果类具有另一个类的引用,则所有引用必须是Serializable,否则将不执行序列化过程.在这种情况下,在运行时抛出NotSerializableException.

例如:

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



7> Sathesh..:

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

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

例如:

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

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



8> 小智..:

序列化是将对象的状态转换为位以使其可以存储在硬盘驱动器上的过程.反序列化同一个对象时,它将在以后保持其状态.它允许您重新创建对象,而无需手动保存对象的属性.

http://en.wikipedia.org/wiki/Serialization

推荐阅读
贾志军
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有