`
i学霸
  • 浏览: 12738 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

I学霸官方免费教程四十六 :Java基础之序列化和反序列化

 
阅读更多

序列化和反序列化

序列化:将对象当前状态转换为字节序列,并通过输出流存储或传输的过程
作用:将对象转换成输出流,永久保存或传输
条件:被序列化的对象的类,实现Serializable接口或Externalizable接口

实例一:
package serializable.s1;
import java.io.Serializable;
/**
 * 用于创建被序列化对象的类
 * @author 学霸联盟 - 赵灿
 */
public class Son implements Serializable {
	// 年龄
	public int age;
	// 颜值
	public int faceValue;

	// 构造方法
	public Son(int age, int faceValue) {
		this.age = age;
		this.faceValue = faceValue;
	}

	// 重写父类(Object类)的toString方法
	@Override
	public String toString() {
		return "age = " + age + "\nfaceValue = " + faceValue;
	}
}
package serializable.s1;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
/**
 * 序列化对象的类
 * @author 学霸联盟 - 赵灿
 */
public class SerializableObject {
	public static void main(String[] args) {
		//创建一个对象son
		Son son = new Son(18);
		//声明文件输出流的引用
		FileOutputStream fos = null;
		//声明对象输出流的引用
		ObjectOutputStream oos = null;
		try {
			//创建文件输出流的对象,其中文件的后缀名可以自定义
			fos = new FileOutputStream("E:\\son.txt");
			//创建对象输出流的对象
			oos = new ObjectOutputStream(fos);
			//将对象写入文件,序列化之后son的age值应为20
			oos.writeObject(son);
			System.out.println("序列化完成!");
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				if (oos != null) {
					//关闭对象输出流
					oos.close();
				}
				if (fos != null) {
					//关闭文件输出流
					fos.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}

反序列化:将序列化产生的字节序列,通过输入流读取,并恢复成对象的过程

package serializable.s1;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectInputStream;
/**
 * 反序列化对象的类
 * @author 学霸联盟 - 赵灿
 */
public class RecoveryObject {
	public static void main(String[] args) {
		Son son = null;
		FileInputStream fis = null;
		ObjectInputStream ois = null;
		try {
			//创建文件输入流对象,这里的文件路径要和序列化时的一致
			fis = new FileInputStream("E:\\son.txt");
			ois = new ObjectInputStream(fis);
			//从对象输入流中读取对象,并强制转换为Son类型
			son = (Son) ois.readObject();
			//输出
			System.out.println(son.toString());
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} finally {
			try {
				if(ois != null){
					//关闭对象输入流
					ois.close();
				}
				if (fis != null) {
					//关闭文件输入流
					fis.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}
输出结果:
age = 18
faceValue = 250

注意:在序列化之后,如果对Son类进行了修改,而没有重新执行序列化操作;此时进行反序列化会出现InvalidClassException(无效的类型异常);
这是因为在序列化时,会给对象加上一个默认的版本号,这个版本号由各属性名,方法名等计算得出;在反序列化时,会验证序列化时产生的版本号,和当前类的版本号是否一致,如果不一致就会产生InvalidClassException


static final long serialVersionUID属性:序列化版本号

作用:用于反序列化时,验证反序列化的对象和当前类是否是同一版本
序列化版本号,可以自己写,也可以使用编译器自动生成的。
自己写这个版本号时要注意,只能写成static final long serialVersionUID这样的,版本号的值可以自定义。
使用Eclipse自动生成,如下图



当设定了版本号,在序列化之后,只要不修改类名和版本号,对类的其他修改,不会使反序列化出现InvalidClassException。

实例二:
package serializable.s2;
import java.io.Serializable;
/**
 * 用于创建被序列化对象的类
 * @author 学霸联盟 - 赵灿
 */
public class Son implements Serializable {
	/**
	 * 编译器自动生成的序列化版本号
	 */
	private static final long serialVersionUID = 5430359291175859046L;
	
	public int age;
	//将实例一的Son类,增加版本号并执行序列化后,将属性faceValue该为fV在执行反序列化
	public int fV;
	//增加name属性
	String name;
	
	public Son(int age, int faceValue) {
		this.age = age;
		this.fV = faceValue;
	}
	
	@Override
	public String toString() {
		//增加name的输出
		return "age = " + age + "\nfV = " + fV + "\nname = " + name;
	}
}
运行结果:
age = 18
fV = 0
name = null
由结果可以看到,被修改和新增的属性值,均为属性的数据类型的默认值。可以理解为删除了faceValue属性,而新增了属性fV和name;反序列化无法获取这些新增属性的值,因为序列化时还没有它们,所以这些属性的值为默认值

关键字transient

使用关键字transient修饰的属性,不会被序列化
实例三:
package serializable.s3;
import java.io.Serializable;
/**
 * 用于创建被序列化对象的类
 * @author 学霸联盟 - 赵灿
 */
public class Son implements Serializable {
	public int age; 
	//将实例一Son中的faceValue属性加上关键字transient修饰
	public transient int faceValue;
	public Son(int age, int faceValue) {
		this.age = age;
		this.faceValue = faceValue;
	}

	@Override
	public String toString() {
		return "age = " + age + "\nfaceValue = " + faceValue;
	}
}
运行结果:
age = 18
faceValue = 0
faceValue的值为0,并不是250;这就是因为使用关键字transient修饰的属性,不会被序列化


序列化父类

使Son类继承Father类
实例四:
package serializable.s4;
import java.io.Serializable;
/**
 * 用于创建被序列化对象的类,继承Father类
 * @author 学霸联盟 - 赵灿
 */
public class Son extends Father implements Serializable {
	public int age;
	public int faceValue;

	public Son(int age, int faceValue) {
		this.age = age;
		this.faceValue = faceValue; 
		//对从Father类继承的money属性赋值
		this.money = 200;
	}
	
	@Override
	public String toString() {
		return "age = " + age + "\nfaceValue = " + faceValue + "\nmoney = "
				+ money;
	}
}

package serializable.s4;
public class Father{
	public int money;
}
运行结果:
age = 18
faceValue = 250
money = 0
package serializable.s4;
import java.io.Serializable;
//增加Father类对Serializable接口的实现
public class Father  implements Serializable {
	public int money;
}
运行结果:
age = 18
faceValue = 250
money = 200

引用类型的属性

Son类中增加一个Friend类型的属性
实例五:
package serializable.s5;
import java.io.Serializable;
/**
 * 用于创建被序列化对象的类
 * @author 学霸联盟 - 赵灿
 */
public class Son implements Serializable {
	public int age;
	public int faceValue;
	//增加Friend属性
	public Friend friend;

	public Son(int age, int faceValue) {
		this.age = age;
		this.faceValue = faceValue;
		//创建Friend对象
		friend = new Friend();
		friend.name = "张三";
	}
	
	@Override
	public String toString() {
		return "age = " + age + "\nfaceValue = " + faceValue + "\nfriendName = " + friend.name;
	}
}

package serializable.s5;
public class Friend{
	public String name;
}
执行序列化操作时会出现NotSerializableException
package serializable.s5;
import java.io.Serializable;
//增加Friend类对Serializable接口的实现
public class Friend  implements Serializable{
	public String name;
}
运行结果:
age = 18
faceValue = 250
friendName = 张三
String类,数组,枚举都实现了Serializable接口,所以可以直接使用


自定义序列化

将实例五的Son该为实现Externalizable接口
实例六:
package serializable.s6;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
/**
 * 用于创建被序列化对象的类
 * @author 学霸联盟 - 赵灿
 */
public class Son implements Externalizable {
	public int age;
	public transient int faceValue;
	public Friend friend; 
	
	//必须包含public的无参构造方法,否则会出现InvalidClassException
	public Son() { }

	public Son(int age, int faceValue) {
		this.age = age;
		this.faceValue = faceValue;
		//创建Friend对象
		friend = new Friend();
		friend.name = "张三";
	}

	/**
	 * 重写Externalizable接口中定义的方法
	 * 向输出流中写属性值
	 */
	@Override
	public void writeExternal(ObjectOutput out) throws IOException {
		//向输出流中写入属性age和faceValue的值
		out.writeInt(age);
		out.writeInt(faceValue);
	}

	/**
	 * 重写Externalizable接口中定义的方法
	 * 读取输入流中的属性值,并赋值给相应的属性
	 * 这里要注意,读属性值的顺序要和写属性值的顺序一致
	 */
	@Override
	public void readExternal(ObjectInput in) throws IOException,
			ClassNotFoundException {
		//从输入流中读取age和faceValue的值,并赋值给相应的属性
		age = in.readInt();
		faceValue = in.readInt();
	}
	
	@Override
	public String toString() {
		return "age = " + age + "\nfaceValue = " + faceValue;
	}
}
package serializable.s6;

public class Friend{
	public String name;
}
输出结果:
age = 18
faceValue = 250


版权声明:本文为博主原创文章,未经博主允许不得转载。

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics