java序列化

什么是序列化

序列化(serialization)是把一个对象的状态写入 一个字节流的过程。当你想要把你的程序状态存 到一个固定的存储区域例如文件时,它是很管用 的。稍后一点时间,你就可以运用序列化过程存 储这些对象将一个对象保存到永久存储设备上(如:文件)称为持久化。(程序储存到文件)

一、为什么需要序列化?

第一种情况是:一般情况下Java对象的声明周期都比Java虚拟机的要短,实际应用中我们希望在JVM停止运行之后能够持久化指定的对象,这时候就需要把对象进行序列化之后保存。

第二种情况是:需要把Java对象通过网络进行传输的时候。因为数据只能够以二进制的形式在网络中进行传输,因此当把对象通过网络发送出去之前需要先序列化成二进制数据,在接收端读到二进制数据之后反序列化成Java对象。

注意事项

假设一个被序列化的对象引用了其他对象,同样, 其他对象又引用了更多的对象。这一系列的对象 和它们的关系形成了一个顺序图表。在这个对象 图表中也有循环引用。也就是说,对象X可以含 有一个对象Y的引用,对象Y同样可以包含一个对 象X的引用。对象同样可以包含它们自己的引用。 对象序列化和反序列化的工具被设计出来并在这 一假定条件下运行良好。如果你试图序列化一个 对象图表中顶层的对象,所有的其他的引用对象 都被循环的定位和序列化。

同样,在反序列化过 程中,所有的这些对象以及它们的引用都被正确 的恢复( 在序列化时,static变量是无法序列化的;如果A包含了
对B的引用,那么在序列化A的时候也会将B一并地序列化;如果此时A可以序列化,B无法序列化,那么当序列化A的时候就会发生异常,这时就需要将对B的引用设为transient,该关键字表示变量不会被序列化)

当一个对象被序列化时,只保存对象的非静态成员变量,不能保存任何的成员方法和静态的成员变量。 如果一个对象的成员变量是一个对象,那么这个 对象的数据成员也会被保存。如果一个可序列化的对象包含对某个不可序列化 的对象的引用,那么整个序列化操作将会失败, 并且会抛出一个NotSerializableException。我们可以将这个引用标记为transient,那么对象仍 然可以序列化,声明成transient的变量不被序列化工具存储。 同样,static变量也不被存储

二、Serializable接口

实现Serializable接口的对象可以被 序列化工具存储和恢复。Serializable接口没 有定义任何成员。它只用来表示一个类可以被 序列化。如果一个类可以序列化,它的所有子 类都可以序列化。一个类若想被序列化,则需要实现java.io.Serializable接口,该接口中没有定义任何方法,是一个标识性接口(Marker Interface),当一个类实现了该接口,就表示这个类的对象是可以序列化的

案例:反序列化时不会调用对象的任何构造方法,仅仅根据所保存的对象状态信息,在内存中重新构建对象

class  Person2  implements  Serializable{
	     int  age;
	     String   name;
	     double   height;
	     public   Person2(int   age,String   name,double   height){
	          this.age=age;
	          this.name=name;
	          this.height=height;
	     }
}

public  class  SerializableTest2 {
	
	     public  static  void main(String[]args)throws     Exception{
	             Person2  p1  = new Person2(20,"zhangsan",4.55);
	             //开始序列化
	             FileOutputStreamf os = new  FileOutputStream("Person2.txt");
	             ObjectOutputStream oos = new  ObjectOutputStream(fos);
	             oos.writeObject(p1);
                 oos.close();
	    
	            //开始反序列化
	            FileInputStreamf  is = new FileInputStream("Person2.txt");
	            ObjectInputStream  ois =new  ObjectInputStream(fis);
	            Person2  p=  (Person2)ois.readObject();
                System.out.println(p.age+","+p.name+","+p.height);20,zhangsan,4.55
                ois.close();
	     }
}

问题1、类添加序列化的ID作用

Java常用于开发分布式应用,分布式应用就涉及到部署的主机的物理位置是不同的,主机应用之间的交互涉及到信息在网络传输的问题,或者应用内部的信息也需要存储到磁盘中;不管是信息的存储或者传输,都是以二进制流的方式进行的。Java对象序列化机制就是把内存中的Java对象(User之类的JavaBean)转换成二进制流。java对象序列化后可以很方便的存储或者在网络中传输。Java的序列化机制是通过运行时判断类的序列化ID(serialVersionUID)来判定版本的一致性。在反序列化时,java虚拟机会通过二进制流中的serialVersionUID与本地的对应的实体类进行比较,如果相同就认为是一致的,可以进行反序列化,正确获得信息,否则抛出序列化版本不一致的异常。所以涉及到数据传输或者存储的类,严格意义上来说都要加上序列化ID,这也是一种良好的编程习惯。纯手打~

三、按自己的想法序列化

1、实现writeObject和readObject

在序列化和反序列化进程中需要特殊处理 的 Serializable 类应该实现以下方法:

private void writeObject(java.io.ObjectOutputStream stream) throws IOException;  private void 

private void readObject(java.io.ObjectInputStream stream) throws IOException, ClassNotFoundException; 

//这两个方法不属于任何一个类和任何一个接口,是非常特 殊的方法.

当我们在一个待序列化/反序列化的类中实现了以上两个private方法(方法声明要与上面的保持完全的一致),那么就允许我们以更

加底层、更加细粒度的方式控制序列化/反序列化的过程。

当我们在上面的案例加入上面2个方法时效果如下

class  Person2  implements  Serializable{
     int age;
     String name;
     double height;

     public Person2(int age,String name,double height){
            this.age=age;
            this.name=name;
            this.height=height;

     }
     private void writeObject(java.io.ObjectOutputStreamout)throws IOException{
            System.out.println("writeobject");

     }
     privatevoidreadObject(java.io.ObjectInputStreamin)throwsIOException,ClassNotFoundException{
          System.out.println("readobject");
     }

}
public  class  SerializableTest2{

   public  static  void  main(String[]  args)throws  Exception{

      Person2  p1 = new Person2(20,"zhangsan",4.55);
      FileOutputStreamf os=newFileOutputStream("Person2.txt");
      ObjectOutputStream oos = newObjectOutputStream(fos);
      oos.writeObject(p1);//writeobject
      oos.close();

      FileInputStream fis=new FileInputStream("Person2.txt");
      ObjectInputStream ois=new ObjectInputStream(fis);
      Person2 p = (Person2)ois.readObject();//readobject
      System.out.println(p.age+","+p.name+","+p.height);//0,null,0.0
      ois.close();
     } 
}

为什么 person的属性是null 和0呢?

现在我们将俩个private方法改如下例

private  void  writeObject(java.io.ObjectOutputStreamout)throws IOException{
  out.writeInt(age);
  out.writeUTF(name);
}

private void  readObject(java.io.ObjectInputStreamin) throws IOException,ClassNotFoundException{
 age = in.readInt();
 name =in.readUTF();
}
//输出如下
//20,zhangsan,0.0

这说明了这两个方法可以帮我们选的我们想要的属性

2、实现Externalizable接口

使用Externalizable进行序列化,当读取对象时,会调用被序列化类的无参构造器去创建一个新的对象,然后再将被保存对象
的字段的值分别填充到新对象中。这就是为什么输出结果中会显示调动了无参构造器。由于这个原因,实现Externalizable接
口的类必须要提供一个无参的构造器,且它的访问权限为public。 Serializable没这个限制

Externalizable 接口定义了两个方法:

//inStream是对象被读取的字节流,outStream是 对象被写入的字节流。
void readExternal(ObjectInput inStream) throws IOException, ClassNotFoundException  
void writeExternal(ObjectOutput outStream) throws IOException 

Externalizable 实例类的惟一特性是可以 被写入序列化流中,该类负责保存和恢复 实例内容。 若某类要完全控制某一对象及 其超类型的流格式和内容,则它要实现 Externalizable 接口的 writeExternal 和 readExternal 方法。这些方法必须显式与 超类型进行协调以保存其状态。这些方法 将代替自定义的 writeObject 和 readObject 方法实现

public   class  Blip3  implements  Externalizable{
   private  int  i;
   private String  s;//Noinitialization

   public  Blip3 (){
      System.out.println("Blip3Constructor");
   }
   public Blip3 (Stringx,inta){
      System.out.println("Blip3(Stringx,inta)");
      s=x;
      i=a;

   }
   public  String  toString(){returns+i;}

   public  void   writeExternal(ObjectOutput  out)throws IOException{

      System.out.println("Blip3.writeExternal");

      out.writeObject(s);

      out.writeInt(i);
   }
   public void readExternal(ObjectInput in)throws IOException,ClassNotFoundException{

      System.out.println("Blip3.readExternal");

      s=(String)in.readObject();

      i=in.readInt();
}
   public static void main(String[] args)throws IOException,ClassNotFoundException{

      System.out.println("Constructingobjects:");

      Blip3b3=newBlip3("AString",47);

      System.out.println(b3);

      ObjectOutputStreamo=newObjectOutputStream(newFileOutputStream("Blip3.out"));

      System.out.println("Savingobject:");

      o.writeObject(b3);

      o.close();

      ObjectInputStreamin=newObjectInputStream(newFileInputStream("Blip3.out"));

      System.out.println("Recoveringb3:");

      b3=(Blip3)in.readObject();

      System.out.println(b3);
   }
}

输出

Constructingobjects:
Blip3(Stringx,inta)
AString47
Savingobject:
Blip3.writeExternal
Recoveringb3:
Blip3Constructor
Blip3.readExternal
AString47
3、transient

将我们认为不应该序列化的非静态属性加上transient关键字,表示这个属性不能序列化,transient关键字只能修饰变量,而不能修饰方法和类

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值