java序列化反序列化学习

两个Java进程在进行远程通信时,彼此可以发送各种类型的数据。无论是何种类型的数据,都会以二进制序列的形式在网络上传送。发送方需要把这个Java对象转换为字节(Byte)序列,才能在网络上传送;接收方将字节序列恢复为Java对象后便可以使用此对象。

把Java对象转换为字节序列的过程称为对象的序列化。把字节序列恢复为Java对象的过程称为对象的反序列化。

对象的序列化主要有两种用途:

1) 把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中;

2) 在网络上传送对象的字节序列,这是RMI和Hessian的基础。

一. JDK中序列化的API

java.io.ObjectOutputStream代表对象输出流,它的writeObject(Object obj)方法可对参数指定的对象参数进行序列化,把得到的字节序列写到一个目标输出流中。

java.io.ObjectInputStream代表对象输入流,它的readObject()方法从一个源输入流中读取字节序列,再把它们反序列化为一个对象,并将其返回。

只有实现了Serializable和Externalizable接口的类的对象才能被序列化。Externalizable接口继承自Serializable接口,实现Externalizable接口的类完全由自身来控制序列化的行为(即自我定制实例变量写入字节流的顺序,除类头外)。要序列化的类实现Serializable接口只是一个标识,并没有实现任何接口的方法。

对象序列化包括如下步骤:

1) 创建一个对象输出流,它可以包装一个其他类型的目标输出流,如FileOutputStream;

2) 通过对象输出流的writeObject()方法写对象。

对象反序列化的步骤如下:

1) 创建一个对象输入流,它可以包装一个其他类型的源输入流,如FileInputStream和InputStream;

2) 通过对象输入流的readObject()方法读取对象。

下面来看例子:

待序列化类包括两个类,User.java和Account.java。其中User包含一个Account类型的成员变量。

public class User implements Serializable{
	/**
	 * 序列化对象1
	 */
	private static final long serialVersionUID = 1L;
	private String name;
	private int age;
	private String sex;
	private Account account;
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	public String getSex() {
		return sex;
	}
	public void setSex(String sex) {
		this.sex = sex;
	}
	public Account getAccount() {
		return account;
	}
	public void setAccount(Account account) {
		this.account = account;
	}
	
	@Override
	public String toString(){
		return(" name :"+this.getName() +
							"\n age is :"+ this.getAge() +
							"\n sex is :"+this.getSex() +
							"\n with account "+ this.getAccount()
							);
	}
	
}

Account.java

public class Account implements Serializable{
	/**
	 * 序列化对象2
	 */
	private static final long serialVersionUID = 1L;
	private int current;
	
	public Account(){
		SAXReader rd = new SAXReader();
		File f = new File("account.xml");
		try {
			Document document = rd.read(f);
			Node node = document.selectSingleNode("/account/current");
			this.setCurrent(Integer.parseInt(node.getText()));
		} catch (DocumentException e) {
			System.out.println("Parsing XML failed!");
			e.printStackTrace();
		}
	}
	public int getCurrent() {
		return current;
	}

	public void setCurrent(int current) {
		this.current = current;
	}
	public String toString(){
		return ("Currenly have "+this.getCurrent());
	}
}

Account在初始化时,首先读取下面的XML文件来set成员变量。

<?xml version="1.0" encoding="GB2312"?>
<account>
	<current>5000</current>
</account>


主函数在TestSerializable.java中,它做的事情是new一个user出来,序列化后保存到一个文件里,然后再读取这个文件反序列化:

public class TestSerializable {
	public static void writeObject(Object o) throws IOException {
		File f = new File ("user.tmp");
		if(f.exists()){
			f.delete();
		}
		FileOutputStream fos = new FileOutputStream(f);
		ObjectOutputStream oos = new ObjectOutputStream(fos);
		oos.writeObject(o);
		oos.flush();
		fos.close();
		oos.close();
	}
	
	public static Object readObject(File f) throws IOException, ClassNotFoundException {
		FileInputStream fis = new FileInputStream(f);
		ObjectInputStream ois = new ObjectInputStream(fis);
		return ois.readObject();
	}
	public static void main(String[] args){
		User u =  new User();
		u.setName("zhangchao");
		u.setAge(10);
		u.setSex("m");
		Account account = new Account();
		u.setAccount(account);
		
		try {
			writeObject(u);
			
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		File f = new File("user.tmp");
		if(!f.exists()){
			System.exit(-1);
		}
		try {
			User oldUser = (User) readObject(f);
			System.out.println(oldUser);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

来看输出结果:

 name :zhangchao
 age is :10
 sex is :m
 with account Currenly have 5000

注意待与系列化的类Account和User必须都实现Serializable接口,否则会报NotSerializableException异常。

当然这只是本地测试,现在进行远程传输。将序列化好后的文件放在一个tomcat应用的根目录下,此例中是(user.tmp)。

TestRemoteSerializable.java的源码如下:

public class TestRemoteSerializable {
	public static void main(String[] args) throws IOException{
		URL url = new URL("http://localhost:8080/workshop/user.tmp");
		InputStream is = url.openStream();
		ObjectInputStream ois = new ObjectInputStream(is);
		User u;
		try {
			u = (User) ois.readObject();
			System.out.println(u);
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}	
	}
}

输出结果:

 name :zhangchao
 age is :10
 sex is :m
 with account Currenly have 5000

最后关于序列化还有三点需要注意:

    a)序列化时,只对对象的状态进行保存,而不管对象的方法;
    b)当一个父类实现序列化,子类自动实现序列化,不需要显式实现Serializable接口;
    c)当一个对象的实例变量引用其他对象,序列化该对象时也把引用对象进行序列化;


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值