序列化

来源:互联网 发布:淘宝订单体检清洗 编辑:程序博客网 时间:2024/05/31 19:47

最近在看关于序列化的东西,下面是从几个博客和书本上组合的,感觉挺有用。

首先是为什么需要序列化

注意:“为避免编译错误,为可序列化的类添加了无参数构造函数。”

MSDN的定义:序列化是将对象状态转换为可保持或可传输的形式的过程。序列化的补集是反序列化,后者将流转换为对象。这两个过程一起保证数据易于存储和传输。

大家关心的是为什么需要序列化,用传统的方法也能实现这种需求吗,它存在的价值是什么,低层的原理、实质、基因的区别是什么?这也是我的疑问,通过在网上搜集,找到了较满意的答案,分享给大家。

答案一:序列化是用来通信的,服务端把数据序列化,发送到客户端,客户端把接收到的数据反序列化后对数据进行操作,完成后再序列化发送到服务端,服务端再反序列化数据后对数据进行操作。说白了,数据需要序列化以后才能在服务端和客户端之间传输。这个服务端和客户端的概念是广义的,可以在网络上,也可以在同一台机器的不同进程中,甚至在同一个进程中进行通信。在传统编程中,对象是通过调用栈间接的与客户端交互,但在面向服务的编程中,客户端永远都不会直接调用实例。不知道说的明不明白。 

好吧,我说的确实不够明白,你问的是为什么需要序列化,我只是说了序列化的一个应用。那我就来说说序列化的好处吧。不序列化也可以传输,但是无法跨平台,安全性也无法保障。我说的是面向服务编程中的作用,在传统编程中,你在表示层实例化一个业务对象,然后调用业务对象中的方法,你想过为什么能这样调用吗?这样做耦合度太高,很不好。如果序列化以后通过特定的协议传输数据就不一样了,表示层通过代理或通道向服务层发送特定的数据格式,这个数据就是序列化以后的,比如XML,服务端接收到以后要进行反序列化,生成服务端可识别的数据格式,比如一个类,然后对数据进行操作,再序列化发送到客户端,客户端再反序列化。这样客户端可以使用和服务端完全不同的开发平台,只要它能够对xml数据进行反序列化,而xml是具有工业标准的数据格式,基本各平台都支持。这也适用于在进程间通信。如果在进程内通信,也可以做到更高的安全性,对象不再通过调用栈交互,而是通过代理或通道。

答案二:这个更进一步的解释了其真正的价值。

简单来说序列化就是一种用来处理对象流的机制,所谓对象流也就是将对象的内容进行流化,流的概念这里不用多说(就是I/O),我们可以对流化后的对象进行读写操作,也可将流化后的对象传输于网络之间(注:要想将对象传输于网络必须进行流化)!在对对象流进行读写操作时会引发一些问题,而序列化机制正是用来解决这些问题的! 

问题的引出: 

如上所述,读写对象会有什么问题呢?比如:我要将对象写入一个磁盘文件而后再将其读出来会有什么问题吗?别急,其中一个最大的问题就是对象引用!举个例子来说:假如我有两个类,分别是A和B,B类中含有一个指向A类对象的引用,现在我们对两个类进行实例化{ A a = new A(); B b = new B(); },这时在内存中实际上分配了两个空间,一个存储对象a,一个存储对象b,接下来我们想将它们写入到磁盘的一个文件中去,就在写入文件时出现了问题!因为对象b包含对对象a的引用,所以系统会自动的将a的数据复制一份到b中,这样的话当我们从文件中恢复对象时(也就是重新加载到内存中)时,内存分配了三个空间,而对象a同时在内存中存在两份,想一想后果吧,如果我想修改对象a的数据的话,那不是还要搜索它的每一份拷贝来达到对象数据的一致性,这不是我们所希望的! 

以下序列化机制的解决方案: 

1.保存到磁盘的所有对象都获得一个序列号(1, 2, 3等等) 

2.当要保存一个对象时,先检查该对象是否被保存了。 

3.如果以前保存过,只需写入"与已经保存的具有序列号x的对象相同"的标记,否则,保存该对象 

通过以上的步骤序列化机制解决了对象引用的问题! 

序列化的实现 

将需要被序列化的类实现Serializable接口,该接口没有需要实现的方法,implements Serializable只是为了标注该对象是可被序列化的,然后使用一个输出流(如:FileOutputStream)来构造一个 ObjectOutputStream(对象流)对象,接着,使用ObjectOutputStream对象的writeObject(Object obj)方法就可以将参数为obj的对象写出(即保存其状态),要恢复的话则用输入流。 



在序列化的过程中,有些数据字段我们不想将其序列化,对于此类字段我们只需要在定义时给它加上transient关键字即可,对于transient字段序列化机制会跳过不会将其写入文件,当然也不可被恢复。但有时我们想将某一字段序列化,但它在SDK中的定义却是不可序列化的类型,这样的话我们也必须把他标注为transient,可是不能写入又怎么恢复呢?好在序列化机制为包含这种特殊问题的类提供了如下的方法定义: 

private void readObject(ObjectInputStream in) throws 

IOException, ClassNotFoundException; 

private void writeObject(ObjectOutputStream out) throws 

IOException; 

(注:这些方法定义时必须是私有的,因为不需要你显示调用,序列化机制会自动调用的) 

使用以上方法我们可以手动对那些你又想序列化又不可以被序列化的数据字段进行写出和读入操作。

总结:对象序列化,就是保证多处需要使用的对象,如果是同一个,就只实例化一次,这样,效率高,如果对象发生了变化,只要序列化的对象流发生对应的变法即可。

什么情况下使用:

有两个最重要的原因促使对序列化的使用:一个原因是将对象的状态保持在存储媒体中,以便可以在以后重新创建精确的副本;另一个原因是通过值将对象从一个应用程序域发送到另一个应用程序域中。

以下引入一个序列化与反序列化的例子

序列化为一般文件,也序列化为XML文件(使用XStream)

用于序列化的实体类Person.java 代码如下(记得需要实现Serializable接口):

import java.io.Serializable;    @SuppressWarnings("serial")  public class Person implements Serializable{      private String name;      private int age;      public Person(){                }      public Person(String str, int n){          System.out.println("Inside Person's Constructor");          name = str;          age = n;      }      String getName(){          return name;      }      int getAge(){          return age;      }  }  

序列化、反序列化为一般的文件,SerializeToFlatFile.java类的代码如下:

import java.io.FileInputStream;  import java.io.FileOutputStream;  import java.io.ObjectInputStream;  import java.io.ObjectOutputStream;      public class SerializeToFlatFile {      public static void main(String[] args) {          SerializeToFlatFile ser = new SerializeToFlatFile();          ser.savePerson();          ser.restorePerson();              }            public void savePerson(){          Person myPerson = new Person("Jay",24);          try {              FileOutputStream fos = new FileOutputStream("E:\\workspace\\2010_03\\src\\myPerson.txt");              ObjectOutputStream oos = new ObjectOutputStream(fos);              System.out.println("Person--Jay,24---Written");              System.out.println("Name is: "+myPerson.getName());              System.out.println("Age is: "+myPerson.getAge());                            oos.writeObject(myPerson);              oos.flush();              oos.close();          } catch (Exception e) {              // TODO: handle exception              e.printStackTrace();          }      }            public void restorePerson() {          try {              FileInputStream fis = new FileInputStream("E:\\workspace\\2010_03\\src\\myPerson.txt");              ObjectInputStream ois = new ObjectInputStream(fis);                            Person myPerson = (Person)ois.readObject();              System.out.println("\n--------------------\n");              System.out.println("Person--Jay,24---Restored");              System.out.println("Name is: "+myPerson.getName());              System.out.println("Age is: "+myPerson.getAge());          } catch (Exception e) {              // TODO: handle exception              e.printStackTrace();          }      }  }  

运行结果为(console输出),当然可以查看到myPerson.txt文件已经生成:
Inside Person's Constructor
Person--Jay,24---Written
Name is: Jay
Age is: 24
--------------------
Person--Jay,24---Restored
Name is: Jay

Age is: 24






0 0
原创粉丝点击