对象的序列化与反序列化深入理解

序列化工具类

 

package serializable;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

/**
 * 对象序列化
 * @author chenyw
 *
 */
public class ObjectSerialize {

	//序列化对象存储路径
	private static final String objectFilePath ="d:\\objectFile.obj";
	
	/**
	 * 序列化对象
	 * @param obj
	 * @throws FileNotFoundException
	 * @throws IOException
	 */
	public void objectOutput(Object obj) throws FileNotFoundException, IOException{
		FileOutputStream fileos = new FileOutputStream(objectFilePath);
		ObjectOutputStream out = new ObjectOutputStream(fileos);
		//序列化对象
		out.writeObject(obj);
		out.close();
	}
	
	/**
	 * 反序列化
	 * @return Object
	 * @throws FileNotFoundException
	 * @throws IOException
	 * @throws ClassNotFoundException
	 */
	public Object objectInput() throws FileNotFoundException, IOException, ClassNotFoundException{
		FileInputStream fileis = new FileInputStream(objectFilePath);
		//反序列化
		ObjectInputStream in = new ObjectInputStream(fileis);
		Object obj = in.readObject();
		in.close();
		return obj;
	}
	/**
	 * @param args
	 * @throws IOException 
	 * @throws FileNotFoundException 
	 */
	public static void main(String[] args) throws Exception {
		ObjectSerialize  os = new ObjectSerialize();
		//..... ....待序列化及反序列化代码
	}

}

 

 

1.序列化注意事项

 

   ObjectOuputStream只能对实现了Serializable接口的类的对象进行序列化,默认情况下,ObjectOuputStream 按照默认方式序列化,这种序列化方式仅仅对对象的非treasient的实例变量进行那个序列化,而不会序列化对象的transient的实例变量,也不会序列化静态变量。

 

 

package serializable;

import java.io.Serializable;

/**
 * 要序列化的对象Customer1
 * 
 * @author chenyw
 *  
 */
public class Customer1 implements Serializable {
  private static int count; //用于计算Customer对象的数目,不被序列化
  private static final int MAX_COUNT=1000;
  private String name;
  private transient String password;//不被序列化
  
  static{
     System.out.println("调用Customer1类的静态代码块");
  }
  public Customer1(){
    System.out.println("调用Customer1类的不带参数的构造方法");
    count++;
  }
  public Customer1(String name, String password) {
    System.out.println("调用Customer1类的带参数的构造方法");
    this.name=name;
    this.password=password;
    count++;
  }
  public String toString() {
    return "count="+count
           +" MAX_COUNT="+MAX_COUNT
           +" name="+name
           +" password="+ password;
  }
}

 

先在ObjectSerialize mian函数下添加,执行对象序列化

 

ObjectSerialize  os = new ObjectSerialize();
Customer1 c = new Customer1("yuwen","123456");
System.out.println("待序列化对象:"+c);
os.objectOutput(c);

 控制台输出:

 

调用Customer1类的静态代码块

调用Customer1类的带参数的构造方法

待序列化对象:count=1 MAX_COUNT=1000 name=yuwen password=123456

 

再ObjectSerialize mian函数下添加,执行对象反序列化

 

ObjectSerialize  os = new ObjectSerialize();
System.out.println("序列化后----");
System.out.println("反序列化后对象:"+os.objectInput());

 控制台输出:

 

序列化后----

调用Customer1类的静态代码块

反序列化后对象:count=0 MAX_COUNT=1000 name=yuwen password=null

 

以上可以通过输出可看到

     (1)transient 关键子的password不被序列化;

     (2)static count不被序列化,当加载Customer类时,count初始化为0。

 

在执行序列化及反序列化不能同时加载同一个类,不然最后反序列化的时候静态变量已经初始化了。

不同时执行序列化及反序列化这样能保证你序列化的类在反序列化之前没有加载,因为静态变量在类加载的时候已经赋值了,所有当处于同一jvm中时,你创建的任何一个对象都拥有这个静态变量。

 

 

2.序列化对象图

 

   类与类之间可能存在关联关系。如下所示的Customer2类与Order2类之间存在一对多的双向关联关系。

 

package serializable;

import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;

/**
 * 客户对象类
 * @author chenyw
 *
 */
public class Customer2 implements Serializable{

	private String name;
	private Set<Order2> orders = new HashSet<Order2>();
	static{
		System.out.println("调用Customer2类的静态代码块");
	}
	public Customer2(){
		System.out.println("调用Customer2类的不带参数的构造方法");
	}
	public Customer2(String name){
		System.out.println("调用Customer2类的带参数的构造方法");
		this.name=name;
	}
	
	public void addOrder(Order2 order){
		orders.add(order);
	}
	
	public String toString(){
		String result = super.toString()+","+orders+"\r\n";
		return result;
	}
}
 

 

package serializable;

import java.io.Serializable;
/**
 * 订单对象类
 * @author chenyw
 *
 */
public class Order2 implements Serializable{

	private String number;
	private Customer2 customer;
	public Order2(){
		System.out.println("调用Order2类的不带参数的构造方法");
	}
	public Order2(String number,Customer2 customer){
		System.out.println("调用Order2类的带参数的构造方法");
		this.number = number;
		this.customer = customer;
	}
}
 

  在ObjectSerialize mian函数添加以下代码,建立他们的关联关系:

 

//客户chenyw有两个订单,订单编号分别为“number1”和“number2”
Customer2 customer = new Customer2("chenyw");
Order2 order1 = new Order2("number1",customer );
Order2 order2 = new Order2("number2",customer );
customer.addOrder(order1);
customer.addOrder(order2);

 进行序列化及反序列化

反序列化后输出如下

    反序列化后对象:serializable.Customer2@60aeb0,[serializable.Order2@89ae9e,      serializable.Order2@1270b73]

 

在默认方式下,对象输出流会对整个对象图进行序列化。当程序执行writeObject(customer)方法时,该方法不仅序列化Customer2对象,还会把两个与它关联的Order2对象也进行序列化。

 

如下图所示,简单的说,在内存中可以从对象A导航到对象B,序列化对象A时,实际上会序列化对象A,以及所有可以从对象A直接或间接导航到的对象。

按照默认方式序列化对象A时,实际上被序列化的对象图中包括:对象A、对象B、对象C、对象D、对象E、对象F和对象G。

3.控制序列化的行为

 

当序列化传输的过程中如用户的密码是保密的不公开的,就得对密码进行加密或不序列化

对象实现以下方法即可自定义序列化

 

 private void writeObject(ObjectOutputStream obj)throws IOException{
        obj.defaultWriteObject();
        //然后做自己的扩展序列化
     }
    
     private void readObject(ObjectInputStream obj) throws IOException,ClassNotFoundException{
       obj.defaultReadObject();
       //然后做自己的拓展反序列化
         
     }

 

【例】在序列化用户时将密码按位取反

 

import java.io.*;
public class Customer3 implements Serializable {
  private static int count; //用于计算Customer3对象的数目
  private static final int MAX_COUNT=1000;
  private String name;
  private transient String password;
  
  static{
     System.out.println("调用Customer3类的静态代码块");
  }
  public Customer3(){
    System.out.println("调用Customer3类的不带参数的构造方法");
    count++;
  }
  public Customer3(String name, String password) {
    System.out.println("调用Customer3类的带参数的构造方法");
    this.name=name;
    this.password=password;
    count++;
  }

  /** 加密数组,将buff数组中的每个字节的每一位取反 
   *  例如13的二进制为00001101,取反后为11110010
   */
  private byte[] change(byte[] buff){
    for(int i=0;i<buff.length;i++){
      int b=0;
      for(int j=0;j<8;j++){
        int bit=(buff[i]>>j & 1)==0 ? 1:0;
        b+=(1<<j)*bit;
      }
      buff[i]=(byte)b;
    }
    return buff;
  }

  private void writeObject(ObjectOutputStream stream)throws IOException {
    stream.defaultWriteObject();  //先按默认方式序列化 
    stream.writeObject(change(password.getBytes()));
    stream.writeInt(count);
  }

  private void readObject(ObjectInputStream stream)
          throws IOException, ClassNotFoundException {
    stream.defaultReadObject();  //先按默认方式反序列化
    byte[] buff=(byte[])stream.readObject();
    password = new String(change(buff));
    count=stream.readInt();
  }

  public String toString() {
    return "count="+count
           +" MAX_COUNT="+MAX_COUNT
           +" name="+name
           +" password="+ password;
  }
}
 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值