java序列化

相信自己能力的人,任何事情都能够做到。


本讲内容:java序列化

举个风筝的例子,放过风筝后,我们会把风筝收起来,把支撑它的骨架拆开,方便我们存放,这就相当于我们Java中的序列化,等到下次你再把它组装起来,还能放,这个过程就是解序列化.


一、Java中对象序列化的作用

为了保存在内存中的各种对象的状态,并且可以把保存的对象状态再读出来。虽然你可以用你自己的各种各样的方法来保存Object States,但是Java给你提供一种应该比你自己好的保存对象状态的机制,那就是序列化。


二、如何实现序列化

用来实现序列化的类都在java.io包中,我们常用的类或接口有:ObjectOutputStream:提供序列化对象并把其写入流的方法,ObjectInputStream:读取流并反序列化对象.Serializable:一个对象想要被序列化,必须实现Serializable接口,目的是声明此类是能被序列化的,自然基础此类的子类也自动的能被序列化.


注意:序列化保存对象的状态,而静态(static)变量不是对象的 状态,所以它们不会被序列化。

示例一:演示一起序列化/反序列化的过程

import java.io.Serializable;

public class Book implements Serializable{
	private int page;
	
	public Book(int page) {
		this.page=page;
	}
	
	public int getpage(){
		return page;
	}
	
	public void setpage(int page){
		this.page=page;
	}
	
	public String toString() {
		 return "Book [page=" + page + "]";  
	}
}

import java.io.Serializable;

public class Student implements Serializable{
	private Book book;
	private String name;
	
	public Student(Book book,String name) {
		this.book=book;
		this.name=name;
	}

	public Book getBook() {
		return book;
	}

	public void setBook(Book book) {
		this.book = book;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
	
	public String toString() {
		 return "Student [book=" + book + ", name=" + name + "]";  
	}
}

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

public class Simulator {//<span style="color: rgb(51, 51, 51); font-family: arial;font-size:18px; line-height: 20px; white-space: pre-wrap;">模拟者类</span>
	public static void main(String[] args) {
		new Simulator().go();
	}
	
	private void go() {
		Student student=new Student(new Book(553), "Dai Code");
		
		try {
			ObjectOutputStream out=new ObjectOutputStream(new FileOutputStream("seria"));
			out.writeObject(student);
			System.out.println("object has been written..");
			out.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		try {
			ObjectInputStream in=new ObjectInputStream(new FileInputStream("seria"));
			Student studentRead=(Student) in.readObject();
			System.out.println("object read here:");  
            System.out.println(studentRead);  
			
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

打印:


可以看到,读取到的对象与保存的对象状态一样。
注意:

1、基本类型 的数据可以直接序列化

2、对象要被序列化,它的类必须要实现Serializable接口;如果一个类中有引用类型的实例变量,这个引用类型也要实现Serializable接口。比如上面 的例子中,Student类中有一个Book类型 的实例就是,要想让Student的对象成功序列化,那么Book也必须要实现Serializable接口。

3、ObjectOutputStreamout  = newObjectOutputStream(new FileOutputStream("seria"));

我们知道 FileOutputStream类有一个带有两个参数的重载Constructor——FileOutputStream(String,boolean),其第二个参数如果为true且String代表的文件存在,那么将把新的内容写到原来文件的末尾而非重写这个文件,这里我们不能用这个版本的构造函数,也就是说我们必须重写这个文件,否则在读取这个文件反序列化的过程中就会抛出异常,导致只有我们第一次写到这个文件中的对象可以被反序列化,之后程序就会出错。


示例二:如果 我们上面 用到的Book类没有实现Serializable接口,但是我们还想序列化Student类的对象 ,怎么办。

Java为我们提供了transient这个关键字。如果一个变量被声明成transient,那么 在序列化的过程 中,这个变量是会被无视的。

public class Book {
	private int page;
	
	public Book(int page) {
		this.page=page;
	}
	
	public int getpage(){
		return page;
	}
	
	public void setpage(int page){
		this.page=page;
	}
	
	public String toString() {
		 return "Book [page=" + page + "]";  
	}
}

public class Student implements Serializable{
	private transient Book book;
	private String name;
	
	public Student(Book book,String name) {
		this.book=book;
		this.name=name;
	}

	public Book getBook() {
		return book;
	}

	public void setBook(Book book) {
		this.book = book;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
	
	public String toString() {
		 return "Student [book=" + book + ", name=" + name + "]";  
	}
}

Simulator.java和上面的一样,我们看一下运行结果:


可以看到,student对象被成功的序列化了。因为序列化过程中跳过了Book实例,所以当恢复对象状态的时候 ,它被赋予了默认值null,这也就意味着我们不能使用它。那如果Book类没有实现Serializable接口,但我们还想对它的状态进行保存,这可以实现 吗?答案是肯定的.


Java针对这种情况有一种特殊的机制—— 一组私有(回调)方法,可以在要被序列化的类中实现它们,在序列化和反序列化的过程中它们会被自动调用。所以在这组方法中我们可以调用ObjectOutputStream/ObjectInputStream的一些有用方法来实现对象状态的保存。

示例三:

Book类和Simulator类都不变,我们来看 一下新的Student类:

public class Student implements Serializable{
	private transient Book book;
	private String name;
	
	public Student(Book book,String name) {
		this.book=book;
		this.name=name;
	}

	public Book getBook() {
		return book;
	}

	public void setBook(Book book) {
		this.book = book;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
	
	//下面这两个方法就是那组特殊的私有方法,它们会在序列化、反序列化的过程 中被自动调用,我们必须保证方法的签名和下面的两个方法完全相同
	
	//这个方法会在序列化的过程中被调用   
	private void writeObject(ObjectOutputStream out){
		try {
			out.defaultWriteObject();//这个方法会把这当前中非静态变量和非transient变量写到流中  
			//因为我们要保存Book的状态,所以我们还要想办法把其状态写入流中
			out.writeInt(book.getpage());//ObjectOutputStream中提供了写基本类型数据的方法  
			//out.close();//注意,这句千万不能有,否刚将直接导致写入失败  
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	//这个方法会在反序列化的过程中被调用 
	private void readObject(ObjectInputStream in){  
		try {
			//和defaultWriteObject()方法相对应,默认的反序列化方法,会从流中读取 非静态变量和非transient变量 
			in.defaultReadObject();  
			 int page  = in.readInt(); //用这个方法来读取一个int型值,这里我们是读取书号  
	         book  = new Book(page); //这里我们就通过读取的 保存的状态构造 了一个和原来一样的Book对象  
	         //in.close();同样的这句也不能有  
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public String toString() {
		 return "Student [book=" + book + ", name=" + name + "]";  
	}
}

注意:写入和读取的顺序一定要对应。像上面如果你是先写基本类型数据的话,那在读取的时候也一定要先读取基本类型的数据

打印:

可以看出Book的状态得到了保存


还有两个问题:

1、如果一个类没有实现Serializable接口,但是它的基类实现 了,这个类可不可以序列化?

2、和上面相反,如果一个类实现了Serializable接口,但是它的父类没有实现 ,这个类可不可以序列化?

 

第1个问题:一个类实现 了某接口,那么它的所有子类都间接实现了此接口,所以它可以被 序列化。

第2个问题:Object是每个类的超类,但是它没有实现 Serializable接口,但是我们照样在序列化对象,所以说明一个类要序列化,它的父类不一定要实现Serializable接口。但是在父类中定义 的状态能被正确 的保存以及读取吗?


http://blog.csdn.net/moreevan/article/details/6698529

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值