在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
这里面需要注意的是,反序列的过程并没有执行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:
最好在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
新增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
反序列化并不能取到值,必须在方法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: