您的位置:首页 > 编程语言 > Java开发

解析java序列化机制

2016-01-11 12:39 519 查看
java序列化

对象序列化机制:把内存中的java对象包装成与平台无关的二进制流,从而允许将二进制流持久保存到磁盘上,或者通过网络将这种二进制流传输到另外的节点,然后再通
过反序列化,将java对象从IO流中恢复。

序列化的必要性: java中,一切皆对象,在分布式环境中需要将Object从这一端网络到另一端,这就需要有一种可以在两端传输数据的协议,java序列化就是为了解决这个
问题。

特点:1,要序列化的对象类需要实现Serializable接口(不需要实现方法,里面是空的,没有方法),相当于声明了一个序列化的标志。

    2,一个对象只能序列化一次,后面对这个对象的重复序列化并不产生作用,仍然返回第一次序列化对象的编号。即使在该对象序列化之后,修改该对象的属性,反序列
化后仍然是第一次序列化后的结果。

序列化的算法:1,将对象实例相关的类元数据输出。(实例化类的域描述)

    2,递归的输出类的超类的描述,直到不再有超类。(父类的域描述)

    3,类元数据输出完之后,开始从最顶层的超类输出对象实例的实际数据值。(从父类开始,实例化对象的实际数据)

    4,从上到下递归输出实例的数据。(递归的直到实例化类,实例化对象的实际数据)

从上面序列化的步骤中可以看出,1,序列化类的父类也必须序列化或者父类的构造函数为空也可,否则不能正常序列化。

     2,如果序列化类的字段涉及到另外的类,那么这个涉及到的类也必须序列化,否则不能正常序列化。

对象序列化步骤:1,创建ObjectOutputStream对象,它需要一个节点流对象。

2,调用writeObj方法,将需要序列化的对象传入其中。

如下:

  //创建一个ObjectOutputStream输出流   
  oos = new ObjectOutputStrea( new FileOutputStrea("object.txt"));   //将序列化后的内容存到object.txt里面
  Person per = new Person("dfy", 500);   //需要序列化的类
  //将per对象写入输出流   
  oos.writeObject(per);


反序列化步骤:1,创建ObjectInputStream对象,它需要一个节点流对象(这里的节点流对象与前面的序列化里面的节点流保持一致)

2,调用readObjec方法,返回的是之前序列化的对象。

如下:

ois=new OutInputStream(new FileInputStream(object.txt));
Person p=(Person)ois.readObject();//从输入流读取java对象,并强制转换为序列化对象的类型(即Person类型)

需要注意的事项:1,反序列化读取的仅仅是Java对象的数据,这里的数据只包括对象的类名和属性,(并不包括方法,静态属性,和transient属性)而不是Java对象类,因
此,在反序列化过程中,必须提供序列化对象所属的Class类文件,在反序列化后,之所以可以调用序列化后的对象,以及没有实现序列化的属性,就是这个
Class文件所起的作用。

2,序列化机制中,读出对象的顺序需要与写入的顺序一致。
3,对象类的父类要么需要序列化,要么要提供无参的构造函数。

对象中的有的属性不想序列化如何办到?

使用关键字transient,如下“

private transient int age;

如何实现自定义序列化:

法一:

public class Person implements java.io.Serializable   
 {   

     private String name;   
     private int age;   
   

     public Person(String name , int age)   
     {   

         System.out.println("有参数的构器");   
         this.name = name;   
         this.age = age;   
     }   
   
     public void setName(String name)   
     {  
         this.name = name;   
     }   

     public String getName()   
     {   
          return this.name;   
     }   
   

     public void setAge(int age)   
     {   

         this.age = age;   
     }   

     public int getAge()   
     {   

          return this.age;   
     }   

  //这里定义了对person对象的序列化机制 

     private void writeObject(java.io.ObjectOutputStream out)   
          throws IOException   
     {   

         out.writeObject(new StringBuffer(name).reverse());   
         out.writeInt(age);   
     }   

 //这里定义了对person对象的序列化机制 

     private void readObject(java.io.ObjectInputStream in)   
          throws IOException, ClassNotFoundException   
     {   

         this.name = ((StringBuffer)in.readObject()).reverse().toString();   
         this.age = in.readInt();   
     }   
 } 
因为Serializable是空接口,里面没有方法,因此当没有添加(注意这里用了添加一词,而不是覆盖,)writeObject和readObject方法时,是实现系统默认的序列化(属性都序列化),当序列化类中添加了writeObject和readObject方法时,对象在序列化和反序列化过程,会自动调用这两个方法,因此我们可以在相应的方法里实现自定义序列化。

法二:实现另外一个接口Externalizable,不需要再实现Serializable,实现Externalizable之后,需要实现writeExternal()和readExternal()两个方法,而不需要实现writeObject和readObject方法。就可以实现自定义序列化。

serialVersionUID的作用:

当序列化对象后,serialVersionUID可以保证,原来的Class类又被修改了,仍然可以正常序列化,不会产生不兼容问题。

如果一个类可序列化,serialVersionUID建议给一个确定的值,不要由系统自动生成,否则在增减字段(不能修改字段类型及长度)时,如果两边的类的版本不同会导致反序列化失败.

参考1

参考2
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: