序列化与自定义序列化

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/skymouse2002/article/details/80935100

说java的序列化一般都是Serializable接口,今天不仅是这部分内容。

第一部分 Serializable接口

首先说一下Serializable接口,类需要实现这个接口,但是需要注意几点

1.如果这个类是可序列化的,那么他的子类也是可以序列化的。

2.如果这个类是可序列化的,他的父类如果没有实现Serializable接口,那么父类的属性不会被序列化,除非父类也实现了Serializable接口。

3.关于类的静态变量,序列化和反序列化不会影响类静态变量的值。

4.Transient 关键字的作用是控制变量的序列化,在变量声明前加上该关键字,可以阻止该变量被序列化到文件中,在被反序列化后,transient 变量的值被设为初始值,如 int 型的是 0,对象型的是 null。

5.关于类的serialVersionUID 的问题,在某些场合,希望类的不同版本对序列化兼容,因此需要确保类的不同版本具有相同的serialVersionUID;而在某些场合,不希望类的不同版本对序列化兼容,因此需要确保类的不同版本具有不同的serialVersionUID。

/**  
* @Title: SerializableTest.java  
* @Package com.digital.base.serializable  
* @Description: TODO(用一句话描述该文件做什么)   
* @date 2018年7月9日  
* @version V1.0  
*/  
package com.digital.base.serializable;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

/**  
* @ClassName: SerializableTest  
* @Description: TODO(这里用一句话描述这个类的作用)  
* @author liucgc  
* @date 2018年7月9日  
*    
*/
public class SerializableTest {
	public static void main(String[] args) throws Exception {
		FileOutputStream fos = new FileOutputStream("temp.out");
		ObjectOutputStream oos = new ObjectOutputStream(fos);
		TestObject testObject = new TestObject();
		testObject.staticValue=500;
		testObject.parentValue=200;
//		TestObject.staticValue=5000;
		System.out.println(testObject.staticValue);
		System.out.println("parentValue is "+testObject.parentValue);
		oos.writeObject(testObject);
		oos.flush();
		oos.close();
		TestObject.staticValue=5000;
		 
		FileInputStream fis = new FileInputStream("temp.out");
		ObjectInputStream ois = new ObjectInputStream(fis);
		TestObject deTest = (TestObject) ois.readObject();
		System.out.println(deTest.testValue);
		System.out.println("parentValue is "+deTest.parentValue);
		System.out.println(deTest.innerObject.innerValue);
		System.out.println(deTest.staticValue);
		}
		}
	 
		class Parent //implements Serializable 
		{
		 
		private static final long serialVersionUID = -4963266899668807475L;
		 
		public int parentValue = 100;
		}
		 
		class InnerObject implements Serializable {
		 
		private static final long serialVersionUID = 5704957411985783570L;
		 
		public int innerValue = 200;
		}
		 
		class TestObject extends Parent implements Serializable {
		 
		private static final long serialVersionUID = -3186721026267206914L;
		 
		public int testValue = 300;
		
		public static int staticValue=50;
		 
		public InnerObject innerObject = new InnerObject();
		}

第二部分 自定义序列化之Serializable

自定义序列化是由ObjectInput/OutputStream在序列化/反序列化时候通过反射检查该类是否存在以下方法(0个或多个):执行顺序从上往下,序列化调用1和2,反序列调用3和4;transient关键字当某个字段被声明为transient后,默认序列化机制就会忽略该字段。

1.Object writeReplace() throws ObjectStreamException;可以通过此方法修改序列化的对象
2.void writeObject(java.io.ObjectOutputStream out) throws IOException; 方法中调用defaultWriteObject() 使用writeObject的默认的序列化方式,除此之外可以加上一些其他的操作,如添加额外的序列化对象到输出:out.writeObject("XX")
3.void readObject(java.io.ObjectInputStream in) throws Exception; 方法中调用defaultReadObject()使用readObject默认的反序列化方式,除此之外可以加上一些其他的操作,如读入额外的序列化对象到输入:in.readObject()

4.Object readResolve() throws ObjectStreamException;可以通过此方法修改返回的对象。

自定义的 writeReplace、writeObject、readResolve 和 readObject 方法可以允许用户控制序列化的过程,下面的例子中模拟了对password字段加密。

/**  
* @Title: Personser.java  
* @Package com.digital.base.serializable  
* @Description: TODO(用一句话描述该文件做什么)  
* @date 2018年7月9日  
* @version V1.0  
*/  
package com.digital.base.serializable;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectInputStream.GetField;
import java.io.ObjectOutputStream;
import java.io.ObjectOutputStream.PutField;
import java.io.Serializable;
import java.util.Date;

/**  
* @ClassName: Personser  
* @Description: TODO(这里用一句话描述这个类的作用)  
* @author liucgc  
* @date 2018年7月9日  
*    
*/
public class Personser implements Serializable {

	private String code;
	private String password="pass";
	private Date createDate;
	/**  
	* @Title: main  
	* @Description: TODO(这里用一句话描述这个方法的作用)  
	* @param @param args    参数  
	* @return void    返回类型  
	* @throws  
	*/
	public static void main(String[] args) {
	    try {
	        ObjectOutputStream out = new ObjectOutputStream(
	                new FileOutputStream("result.obj"));
	        Personser per=new Personser();
	        per.setPassword("password123");
	        out.writeObject(per);
	        out.close();
	 
	        ObjectInputStream oin = new ObjectInputStream(new FileInputStream(
	                "result.obj"));
	        Personser t = (Personser) oin.readObject();
	        System.out.println("解密后的字符串:" + t.getPassword());
	        oin.close();
	    } catch (FileNotFoundException e) {
	        e.printStackTrace();
	    } catch (IOException e) {
	        e.printStackTrace();
	    } catch (ClassNotFoundException e) {
	        e.printStackTrace();
	    }
	}

	/**
	 * @return the code
	 */
	public String getCode() {
		return code;
	}

	/**
	 * @param code the code to set
	 */
	public void setCode(String code) {
		this.code = code;
	}

	/**
	 * @return the password
	 */
	public String getPassword() {
		return password;
	}

	/**
	 * @param password the password to set
	 */
	public void setPassword(String password) {
		this.password = password;
	}

	/**
	 * @return the createDate
	 */
	public Date getCreateDate() {
		return createDate;
	}

	/**
	 * @param createDate the createDate to set
	 */
	public void setCreateDate(Date createDate) {
		this.createDate = createDate;
	}

	private void writeObject(ObjectOutputStream out) {
	    try {
	        PutField putFields = out.putFields();
	        System.out.println("原密码:" + password);
	        password = enCode(password);//模拟加密
	        putFields.put("password", password);
	        System.out.println("加密后的密码" + password);
	        out.writeFields();
	    } catch (IOException e) {
	        e.printStackTrace();
	    }
	}
	private void readObject(ObjectInputStream in) {
	    try {
	        GetField readFields = in.readFields();
	        
	        Object object = readFields.get("password", "encryption");
	        System.out.println("要解密的字符串:" + object.toString());
	        password=object.toString();
	        password=deCode(password);//模拟解密,需要获得本地的密钥

	    } catch (IOException e) {
	        e.printStackTrace();
	    } catch (ClassNotFoundException e) {
	        e.printStackTrace();
	    }
	}
	public String enCode(String pass) {
		
		if("password123".equals(pass)) {
			return "encryption";
		}
		return "error";
	}
	public String deCode(String pass) {
		
		if("encryption".equals(pass)) {
			return "password123";
		}
		return "error";
	}
}

单例模式的类实现序列化接口,若使用默认的序列化策略,则在反序列化返回的对象创建了新的对象,破坏了单例模式,面试扩展:如果一个类是单例的,那么如何能再创建该类一个新的对象。

    private Object readResolve() throws ObjectStreamException {
        System.out.println("4 read resolve start");
        return PersonSingleton.getInstance();//不管序列化的操作是什么,返回的都是本地的单例对象
    }

第三部分 自定义之 Externalizable接口

此种实现方法需要注意的两点,

1.类必须存在一个pulic的无参数构造方法。

2.反序列化时的字段属性需要与序列化时一致,否则值顺序错乱。

/**  
* @Title: Personextern.java  
* @Package com.digital.base.serializable  
* @Description: TODO(用一句话描述该文件做什么)  
* @author liucgc  
* @date 2018年7月9日  
* @version V1.0  
*/  
package com.digital.base.serializable;

import java.io.Externalizable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.util.Date;

/**  
* @ClassName: Personextern  
* @Description: TODO(这里用一句话描述这个类的作用)  
* @author liucgc  
* @date 2018年7月9日  
*    
*/
public class Personextern implements Externalizable {
	private String code;
	private String password="pass";
	private Date createDate;
	public Personextern() {
		System.out.println("Personextern constructor1");
	}
	public Personextern(String code,String password,Date createDate) {
		this.code=code;
		this.password=password;
		this.createDate=createDate;
		System.out.println("Personextern constructor2");
	}

	public void writeExternal(ObjectOutput out) throws IOException {
		// TODO Auto-generated method stub
		out.writeObject(code);
		out.writeObject(password);
		out.writeObject(createDate);
	}

	public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {		
		code=(String) in.readObject();
		password=(String) in.readObject();
		createDate=(Date)in.readObject();
	}

	/**
	 * @throws IOException   
	* @Title: main  
	* @Description: TODO(这里用一句话描述这个方法的作用)  
	* @param @param args    参数  
	* @return void    返回类型  
	* @throws  
	*/
	public static void main(String[] args) throws IOException {
		FileOutputStream fos=null;
        ObjectOutputStream oos=null;
        FileInputStream fis=null;
        ObjectInputStream ois=null;
        try {
	        Personextern per=new Personextern();
	        per.setPassword("password123");
	        per.setCode("123123");
            fos=new FileOutputStream(new File("E://javanewlskd.txt"));
            oos=new ObjectOutputStream(fos);
            oos.writeObject(per);
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
            if(oos!=null) {
                oos.close();
            }
        }
        
        try {
            fis=new FileInputStream(new File("E://javanewlskd.txt"));
            ois=new ObjectInputStream(fis);
            System.out.println("反序列化之后,readObject");
            Personextern per=(Personextern)ois.readObject();
            System.out.println(per);
        } catch(Exception e) {
            e.printStackTrace();
        } finally {
            if(ois!=null) {
                ois.close();
            }
        }        
	}

	@Override
	public String toString() {
		return "Personextern [code=" + code + ", password=" + password + ", createDate=" + createDate + "]";
	}

	/**
	 * @return the code
	 */
	public String getCode() {
		return code;
	}

	/**
	 * @param code the code to set
	 */
	public void setCode(String code) {
		this.code = code;
	}

	/**
	 * @return the password
	 */
	public String getPassword() {
		return password;
	}

	/**
	 * @param password the password to set
	 */
	public void setPassword(String password) {
		this.password = password;
	}

	/**
	 * @return the createDate
	 */
	public Date getCreateDate() {
		return createDate;
	}

	/**
	 * @param createDate the createDate to set
	 */
	public void setCreateDate(Date createDate) {
		this.createDate = createDate;
	}

}

第四部分 性能对比

/**  
* @Title: TestPerformance.java  
* @Package com.digital.base.serializable  
* @Description: TODO(用一句话描述该文件做什么)  
* @author liucgc  
* @date 2018年7月9日  
* @version V1.0  
*/  
package com.digital.base.serializable;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
/**  
* @ClassName: TestPerformance  
* @Description: TODO(这里用一句话描述这个类的作用)  
* @author liucgc  
* @date 2018年7月9日  
*    
*/
public class TestPerformance {

	/**
	 * @throws IOException   
	* @Title: main  
	* @Description: TODO(这里用一句话描述这个方法的作用)  
	* @param @param args    参数  
	* @return void    返回类型  
	* @throws  
	*/
	public static void main(String[] args) throws IOException {
        long start =  System.currentTimeMillis();  
        int count=10000;
        start =  System.currentTimeMillis();
        setSerializableObject(count);  
        System.out.println("java原生序列化时间:" + (System.currentTimeMillis() - start) + " ms" );    
        start =  System.currentTimeMillis();  
        getSerializableObject(count);  
        System.out.println("java原生反序列化时间:" + (System.currentTimeMillis() - start) + " ms");
        
        start =  System.currentTimeMillis();
        setSerializableObjectExt(count);  
        System.out.println("java Externalizable序列化时间:" + (System.currentTimeMillis() - start) + " ms" );    
        start =  System.currentTimeMillis();  
        getSerializableObjectExt(count);  
        System.out.println("java Externalizable反序列化时间:" + (System.currentTimeMillis() - start) + " ms"); 

	}
	/**  
	* @Title: getSerializableObject  
	* @Description: TODO(这里用一句话描述这个方法的作用)  
	* @param @param count    参数  
	* @return void    返回类型  
	* @throws  
	*/  
	private static void getSerializableObject(int count) {
	    try {
	        ObjectInputStream oin = new ObjectInputStream(new FileInputStream(
	                "result.obj"));
	        for(int i=0;i<count;i++) {
	        Personser t = (Personser) oin.readObject();
	        }
	        oin.close();
	    } catch (FileNotFoundException e) {
	        e.printStackTrace();
	    } catch (IOException e) {
	        e.printStackTrace();
	    } catch (ClassNotFoundException e) {
	        e.printStackTrace();
	    }		
	}
	/**  
	* @Title: setSerializableObject  
	* @Description: TODO(这里用一句话描述这个方法的作用)  
	* @param @param count    参数  
	* @return void    返回类型  
	* @throws  
	*/  
	private static void setSerializableObject(int count) {
	    try {
	        ObjectOutputStream out = new ObjectOutputStream(
	                new FileOutputStream("result.obj"));
	        for(int i=0;i<count;i++) {
		        Personser per=new Personser();
		        per.setPassword("password123");
		        out.writeObject(per);	
	        }
	        out.close();
	    } catch (FileNotFoundException e) {
	        e.printStackTrace();
	    } catch (IOException e) {
	        e.printStackTrace();
	    }		
	}
	/**
	 * @throws IOException   
	* @Title: getSerializableObject  
	* @Description: TODO(这里用一句话描述这个方法的作用)  
	* @param @param count    参数  
	* @return void    返回类型  
	* @throws  
	*/  
	private static void getSerializableObjectExt(int count) throws IOException {
		FileOutputStream fos=null;
        ObjectOutputStream oos=null;
        FileInputStream fis=null;
        ObjectInputStream ois=null;
        try {
            fis=new FileInputStream(new File("ext.obj"));
            ois=new ObjectInputStream(fis);
            for(int i=0;i<count;i++) {            
            Personextern per=(Personextern)ois.readObject();
            }
        } catch(Exception e) {
            e.printStackTrace();
        } finally {
            if(ois!=null) {
                ois.close();
            }
        } 		
	}
	public static void setSerializableObjectExt(int count) throws IOException{ 
		FileOutputStream fos=null;
        ObjectOutputStream oos=null;
        FileInputStream fis=null;
        ObjectInputStream ois=null;
        try {
        	fos=new FileOutputStream(new File("ext.obj"));
        	oos=new ObjectOutputStream(fos);
        	for(int i=0;i<count;i++) {
        		Personextern per=new Personextern();
    	        per.setPassword("password"+i);
    	        per.setCode("code"+i);  
                oos.writeObject(per);
        	}              
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
            if(oos!=null) {
                oos.close();
            }
        }
	}

}

自定义序列化效率更低一些。

java Externalizable序列化时间:214 ms
java Externalizable反序列化时间:255 ms
java原生序列化时间:85 ms
java原生反序列化时间:166 ms


java原生序列化时间:106 ms
java原生反序列化时间:164 ms
java Externalizable序列化时间:196 ms
java Externalizable反序列化时间:229 ms

参考网页相关地址

https://www.cnblogs.com/yoohot/p/6019767.html微笑https://www.cnblogs.com/chenfei0801/archive/2013/04/06/3002146.html

http://www.cnblogs.com/dukc/p/4817822.html




猜你喜欢

转载自blog.csdn.net/skymouse2002/article/details/80935100