java序列化进阶

      在java中,通常情况下,一旦一个程序运行结束,生成的对象也会消失。如果想永久的保存对象,可以将对象序列化,在需要的时候在进行反序列化。java类实现序列化的方法非常简单,只需要实现Serializable即可。Serializable是一个接口,没有任何的方法。序列化只需要构建一个ObjectOutputStream,然后执行ObjectOutputStream的writeObject()方法。反序列化就是执行相反的方法,构建一个ObjectInputStream,然后执行readObject()方法。
 
 
下面的实例中都会用到一个test的方法,如下
package com.my.web.server;

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

import com.my.web.model.UserInfo;

/**
 * 序列化ser
 * @author zhangxiuxiu
 *
 */
public class SerializableSer {
	
	public static void main(String[] args) throws IOException, ClassNotFoundException {
		SerializableSer ser = new SerializableSer();
		ser.serializable();
		ser.deserialize();
	}

	/**
	 * 序列化
	 * @throws IOException 
	 */
	public void serializable() throws IOException{
		UserInfo u = new UserInfo("1", "zxx");
		u.setPassword("zxx");
		File file = new File("userinfo.txt");
		
		file.createNewFile();
		
		//序列化的过程
		FileOutputStream fileOutputStream = new FileOutputStream(file);
		ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
		objectOutputStream.writeObject(u);
		objectOutputStream.flush();
		objectOutputStream.close();
		fileOutputStream.close();
	}
	
	
	/**
	 * 反列化
	 * @throws IOException 
	 * @throws ClassNotFoundException 
	 */
	public void deserialize() throws IOException, ClassNotFoundException{
		File file = new File("userinfo.txt");
		
		FileInputStream fis = new FileInputStream(file);
		ObjectInputStream ois = new ObjectInputStream(fis);
		
		UserInfo ui = (UserInfo)ois.readObject();
		System.out.println(ui.toString());
		
		ois.close();
		fis.close();
	}
	
	
}

  

 
 
 
一、实现Serializable
         一个类实现Serializable了之后就可以很容易的进行序列化和反序列化的操作。这里面需要注意的是,反序列的过程并没有执行model的默认构造方法,只是通过流的方式来生成一个model类。所以并不要求model类有无参的构造方法。
  下面的例子中,UserInfo这个model中没有无参的构造方法,但是可以正常的进行序列化和反序列化。反序列化的时候不会调用UserInfo的构造方法
         
package com.my.web.model;

import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.io.Serializable;
import java.sql.Date;

public class UserInfo implements Serializable{

	/**
	 * 
	 */
	private static final long serialVersionUID = -7448507529918501715L;
	private String id;
	private String userName;
	private Date date;
	private transient String password;
	
	
	
	
	public UserInfo(String id,String userName){
		System.out.println("有参数的构造方法:"+id+"--------"+userName);
		this.id = id;
		this.userName = userName;
	}
	
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}
	public Date getDate() {
		return date;
	}
	public void setDate(Date date) {
		this.date = date;
	}
	public String getId() {
		return id;
	}
	public void setId(String id) {
		this.id = id;
	}
	public String getUserName() {
		return userName;
	}
	public void setUserName(String userName) {
		this.userName = userName;
	}

	public String toString(){
		return "id:"+id+";userName:"+userName+";password:"+password+";date:"+(date==null?"":date.getTime());
	}
	
	
	
}

  

  执行上面的test方法,结果如下

有参数的构造方法:1--------zxx
id:1;userName:zxx;password:null;date:

  

  使用实现Serializable的方法进行序列化的时候,最好在model类中增加serialVersionUID的属性。如果model中没有该属性,在进行序列化的时候,会根据model中的属性、方法通过算法生成一个serialVersionUID。这样会存在一个问题:如果先将该model序列化到了一个文件中,然后中间修改了model中的属性,比如新增了一个属性,在用该文件中的流进行反序列化的时候,会出现一个错误,这是因为序列化和反序列的时候算法生成的serialVersionUID不一致。所以如果生成了一个固定的serialVersionUID,就可以正确进行反序列化。

     报错如下图:

Exception in thread "main" java.io.InvalidClassException: com.my.web.model.UserInfo; local class incompatible: stream classdesc serialVersionUID = -7448507529918501715, local class serialVersionUID = 7932322403676295014

     

      model里面可以新增writeObject和readObject方法,注意这里是用的新增,不是重写,因为Serializable中是没有这两个方法的。如果新增了这两个方法,在进行序列化的时候会执writeObject这个方法,就会不去执行默认的序列化方法,如果需要执行默认的方法,只需要增加out.defaultWriteObject();即可。反序列过程有类似的ois.defaultReadObject();方法,具体事例见下图

     

package com.my.web.model;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.sql.Date;

public class UserInfo implements Serializable{

	/**
	 * 
	 */
	private static final long serialVersionUID = -7448507529918501715L;
	private String id;
	private String userName;
	private String password;
	private Date date;
	
	
	
	private void writeObject(ObjectOutputStream out) throws IOException {
		out.defaultWriteObject();
		System.out.println("-----序列化-----");
	}
	
	private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException{
		ois.defaultReadObject();
		System.out.println("-----反序列化-----");
    }

	
	public UserInfo(String id,String userName){
		System.out.println(id+"--------"+userName);
		this.id = id;
		this.userName = userName;
	}

	public Date getDate() {
		return date;
	}
	public void setDate(Date date) {
		this.date = date;
	}
	
	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	public String getId() {
		return id;
	}
	public void setId(String id) {
		this.id = id;
	}
	public String getUserName() {
		return userName;
	}
	public void setUserName(String userName) {
		this.userName = userName;
	}
	
	public String toString(){
		return "id:"+id+";userName:"+userName+";password:"+password+";date:"+(date==null?"":date.getTime());
	}
	
	
}

  

  

  打印结果如下图

  

1--------zxx
-----序列化-----
-----反序列化-----
id:1;userName:zxx;password:zxx;date:

  当某个字段被声明为transient后,默认序列化机制就会忽略该字段。

package com.my.web.model;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.sql.Date;

public class UserInfo implements Serializable{

	/**
	 * 
	 */
	private static final long serialVersionUID = -7448507529918501715L;
	private String id;
	private String userName;
	private Integer age;
	private String address;
	private Date date;
	private transient String password;
	
	
	
	private void writeObject(ObjectOutputStream out) throws IOException {
		out.defaultWriteObject();
		//just do it 
		out.writeObject(password);
		System.out.println("-----序列化-----");
	}
	
	private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException{
		ois.defaultReadObject();
		//just do it 
		this.password = (String)ois.readObject();
		System.out.println("-----反序列化-----");
    }

	
	public UserInfo(String id,String userName){
		System.out.println(id+"--------"+userName);
		this.id = id;
		this.userName = userName;
	}

	
	
	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	public Date getDate() {
		return date;
	}
	public void setDate(Date date) {
		this.date = date;
	}
	public Integer getAge() {
		return age;
	}
	public void setAge(Integer age) {
		this.age = age;
	}
	public String getAddress() {
		return address;
	}
	public void setAddress(String address) {
		this.address = address;
	}
	public String getId() {
		return id;
	}
	public void setId(String id) {
		this.id = id;
	}
	public String getUserName() {
		return userName;
	}
	public void setUserName(String userName) {
		this.userName = userName;
	}
	
	public String toString(){
		return "id:"+id+";userName:"+userName+";password:"+password+";date:"+(date==null?"":date.getTime());
	}
	
	
	
}

  

   

  在上面的代码中,运行的结果如下

1--------zxx
-----序列化-----
-----反序列化-----
id:1;userName:zxx;password:zxx;date:

  但是,如果将just do it 下面红色的代码注释掉,运行的结果如下

1--------zxx
-----序列化-----
-----反序列化-----
id:1;userName:zxx;password:null;date:

  

二、实现Externalizable

  通过实现Externalizable进行序列化和反序列的操作。这个在进行反序列的过程的时候,需要调用model的默认构造方法的,所以model一定要有public的无参构造方法,如果没有无参构造方法,在进行反序列化的时候,会提示错误。

Exception in thread "main" java.io.InvalidClassException: com.my.web.model.UserInfo; no valid constructor

  有了无参数的构造方法,可以保证该model正常的进行序列化和反序列化,但是反序列化并不能取到值,必须在方法readExternal()中进行手动赋值

      

package com.my.web.model;

import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.sql.Date;

public class UserInfo implements Externalizable{

	/**
	 * 
	 */
	private static final long serialVersionUID = -7448507529918501715L;
	private String id;
	private String userName;
	private Date date;
	private transient String password;
	
	public UserInfo(){
		System.out.println("无参数的构造方法");
	}
	
	public UserInfo(String id,String userName){
		System.out.println("有参数的构造方法:"+id+"--------"+userName);
		this.id = id;
		this.userName = userName;
	}
	
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}
	public Date getDate() {
		return date;
	}
	public void setDate(Date date) {
		this.date = date;
	}
	public String getId() {
		return id;
	}
	public void setId(String id) {
		this.id = id;
	}
	public String getUserName() {
		return userName;
	}
	public void setUserName(String userName) {
		this.userName = userName;
	}

	public String toString(){
		return "id:"+id+";userName:"+userName+";password:"+password+";date:"+(date==null?"":date.getTime());
	}
	

	@Override
	public void writeExternal(ObjectOutput out) throws IOException {
		// TODO Auto-generated method stub
		out.writeObject(id);
		out.writeObject(userName);
		
	}


	@Override
	public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
		// TODO Auto-generated method stub
		this.id = (String)in.readObject();
		this.userName = (String)in.readObject();
	}
	
	
	
	
}

  

  上述的执行结果

有参数的构造方法:1--------zxx
无参数的构造方法
id:1;userName:zxx;password:null;date:

  如果把上面红色的代码注释掉,执行结果

有参数的构造方法:1--------zxx
无参数的构造方法
id:null;userName:null;password:null;date:

  

 
原文地址:https://www.cnblogs.com/hyzxx/p/7999250.html