Java对象的序列化和反序列化
当两个进程在进行远程通信时,彼此可以发送各种类型的数据。无论是何种类型的数据,都会以二进制序列的形式在网络上传送。发送方需要把这个Java对象转换为字节序列,才能在网络上传送;接收方则需要把字节序列再恢复为Java对象。只能将支持 java.io.Serializable 接口的对象写入流中。每个 serializable 对象的类都被编码,编码内容包括类名和类签名、对象的字段值和数组值,以及从初始对象中引用的其他所有对象的闭包。
序列化:把Java对象转换为字节序列的过程。
反序列化:把字节序列恢复为Java对象的过程。
对象的序列化主要有两种用途:
1) 把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中;
2) 在网络上传送对象的字节序列。
对象序列化
1、序列化API
java.io.ObjectOutputStream代表对象输出流,它的writeObject(Object obj)方法可对参数指定的obj对象进行序列化,把得到的字节序列写到一个目标输出流中。只有实现了Serializable和Externalizable接口的类的对象才能被序列化。
java.io.ObjectInputStream代表对象输入流,它的readObject()方法从一个源输入流中读取字节序列,再把它们反序列化为一个对象,并将其返回。
2、示例代码:
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
/*
* 对象的序列化与反序列化
*/
public class ObjectStreamTest {
public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
// TODO Auto-generated method stub
Employee harry=new Employee("Harry Hacker", "女", 50000, 1989, 5, 6);
Manager carl=new Manager("Carl Cracker", "男", 80000, 1987, 12, 15);
carl.setSecretary(harry);
carl.setBonus(500);
Manager tony=new Manager("Tony Tester", "男", 40000, 1990, 3, 15);
tony.setBonus(1000);
tony.setSecretary(harry);
Employee[] staff=new Employee[3];
staff[0]=carl;
staff[1]=harry;
staff[2]=tony;
// 序列化前
for(Employee e:staff){
System.out.println(e.toString());
}
System.out.println("\n\n");
try(ObjectOutputStream out=new ObjectOutputStream(new FileOutputStream("employee.dat")))
{
out.writeObject(staff);
}
Employee[] newStaff=null;
try(ObjectInputStream in=new ObjectInputStream(new FileInputStream("employee.dat")))
{
newStaff=(Employee[]) in.readObject();
newStaff[1].raiseSalary(10);
}
// 反序列化后
for(Employee e:newStaff){
System.out.println(e.toString());
}
}
}
其中Employee类和Manager 类定义如下:
Employee类
import java.io.Serializable;
import java.sql.Date;
public class Employee implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
private String name;
private String gender;
private double salary;
private Date hireDay;
public Employee(String n,String g,double s,int y,int m,int d){
this.name=n;
this.gender=g;
this.salary=s;
@SuppressWarnings("deprecation")
Date day=new Date(y-1900, m-1, d);
this.hireDay=day;
}
public String getName(){
return name;
}
public double getSalary(){
return salary;
}
public String getGender(){
return gender;
}
public Date getHireDay(){
return hireDay;
}
public void raiseSalary(double byPercent){
double raise=salary*byPercent/100;
salary+=raise;
}
public String toString(){
String str=name+":"+gender+", "+salary+", "+hireDay.toString();
return str;
}
}
Manager 类
public class Manager extends Employee {
/**
*
*/
private static final long serialVersionUID = 1L;
private Employee secretary;
private double bonus;
public Manager(String n,String g,double s,int y,int m,int d){
super(n, g, s, y, m, d);
bonus=0;
secretary=null;
}
public double getSalary(){
double baseSalary=super.getSalary();
return baseSalary+bonus;
}
public void setBonus(double b){
bonus=b;
}
public void setSecretary(Employee em){
this.secretary=em;
}
public Employee getSecretary(){
return secretary;
}
public String toString(){
String str=super.toString();
String str1=secretary.getName();
return str+" 秘书:"+str1;
}
}
对象序列化用途之一—-克隆
序列化机制有一个很有趣的用法:提供了一种克隆对象的简便途径,只要对应的类是可序列化的即可。其做法很简单:直接将对象序列化到输出流中,然后将其读回。这样产生的新对象是对现有对象的一个深拷贝。在此过程中,我们不必将对象写入到一个文件中,因为可以用ByteArrayOutputStream将数据保存到字节数组中。
下面的示例代码展现了这一过程:
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Date;
import java.util.GregorianCalendar;
public class SerialCloneTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
Person dandan=new Person("DanDan", "female", 1991, 8, 29);
Person jiayan=(Person)dandan.clone();
jiayan.setName("JiaYan");
System.out.println(dandan);
System.out.println(jiayan);
}
}
class SerialCloneable implements Cloneable,Serializable
{
private static final long serialVersionUID = 1L;
public Object clone(){
try{
// save the object to a byte array
ByteArrayOutputStream bout=new ByteArrayOutputStream();
ObjectOutputStream out=new ObjectOutputStream(bout);
out.writeObject(this);
out.close();
// read a clone of the object from the byte array
ByteArrayInputStream bin=new ByteArrayInputStream(bout.toByteArray());
ObjectInputStream in=new ObjectInputStream(bin);
Object ret=in.readObject();
in.close();
return ret;
}catch(Exception e){
return null;
}
}
}
/**
要被克隆的对象需要继承SerialCloneable类
*/
class Person extends SerialCloneable
{
private static final long serialVersionUID = 1L;
private String name;
private String gender;
private Date birthDay;
public Person(String n,String g,int year,int month,int day)
{
name=n;
gender=g;
GregorianCalendar calendar=new GregorianCalendar(year,month-1,day);
birthDay=calendar.getTime();
}
public String getName(){
return name;
}
public String getGender(){
return gender;
}
public Date getBirthDay(){
return birthDay;
}
public void setName(String newName){
name=newName;
}
public String toString()
{
return getClass().getName()
+"[name="+name
+",gender="+gender
+"birthDay="+birthDay
+"]";
}
}