Object Clone and Serialization
对象复制与对象序列化
LiFan(李凡)
Chengdu University of Information Technology
The Department Of Computer Science
2005
Cloning Objects
? 复制对象 指创建一个新的与指定对象具有相
同状态的对象副本 (Copy),又称为 克隆对象
? 在程序设计中通常可以使用以下方式实现克
隆对象,
(1) 使用拷贝构造函数 (Copy Constructor)
(2) 使用拷贝工厂方法 (Copy Factory Method)
(3) 使用 Java语言提供的对象克隆机制
Copy Constructor
? 拷贝构造函数 是指以类自身的引用类型作为
参数,在构造函数中将作为参数的对象的状
态 (对象的域 )赋值给新建的对象
? 在拷贝构造函数中可以直接访问作为参数的
对象的任何域
class Sheep{
private String name;
private float weight;
private String color;
public Sheep(String name,float weight,String color){
this.name=name;
this.weight=weight;
this.color=color;
}
public Sheep(Sheep other){
this.name=other.name;
this.weight=other.weight;
this.color=other.color;
}
// …… 其它方法
}
拷贝构造函数
Sheep sheepA=new Sheep(“XiaoBai”,45,“White”);
Sheep sheepB=new Sheep(sheepA);
Copy Factory Method
? 拷贝工厂方法 与拷贝构造函数类似,其区别
是拷贝工厂方法是一个静态方法,该方法返
回所创建的对象副本
? 拷贝工厂方法中适用于 JavaBean风格的类的
对象复制
class Cat{
private String name;
private String color;
public String getName() { return name; }
public void setName(String newName) { name=newName; }
public String getColor() { return color; }
public void setColor(String newColor) { color=newColor; }
public static Cat copyCat(Cat other){
Cat cat=new Cat();
cat.setName(other.getName());
cat.setColor(other.getColor());
}
}
Cat catA=new Cat();
catA.setName(“MiMi”);
catA.setColor(“Black”);
Cat catB=Cat.copyCat(catA);
拷贝工厂方法
Shortcoming Of Copy Constructor
? 拷贝构造函数的缺点,
(1) 必须针对对象的每个域进行赋值操作
(2) 无法访问父类或祖先类中私有的或被隐
藏的受保护的域
? 拷贝构造函数或拷贝工厂方法只适用于简单
的类的对象的复制
class SuperClass{
private int a;
protected int b;
SuperClass(int i,int j){
a=i;
b=j;
}
}
class SubClass extends SuperClass{
private int b;
SubClass(int i,int j,int k){
super(i,j);
b=k;
}
SubClass(SubClass sub){
super(sub.a,sub.b);
b=sub.b;
}
}
错误
Java Object Cloning Mechanism
? Java语言在语言级提供了一种对象
复制机制,类的对象要支持复制,
必须使用 接口 java.lang.Cloneable和
类 Object的 clone()方法
java.lang.Cloneable Interface
? 接口 java.lang.Cloneable是一个 标记接口
? JVM将对 实现了 java.lang.Cloneable接口并且
提供了 public的 clone()方法 的类的对象提供
复制支持
class <类名 > implements Cloneable{
// ……
public Object clone() { … }
}
Object.clone() Method
? 类 Object的 clone()方法 如下,
protected Object clone()
throws CloneNotSupportedExdeption
? 实现了 java.lang.Cloneable接口的类的对象调
用 Object.clone()方法将返回当前对象的一个
影子拷贝 (Shallow Copy),这个拷贝中所有
基本数据类型的域与源对象相同
class Sheep implements Cloneable{
private String name;
private float weight;
private String color;
public Sheep(String name,float weight,String color){
this.name=name;
this.weight=weight;
this.color=color;
}
public Object clone(){
try{
return super.clone();
} catch(CloneNotSupportedException e) {
throw new InternalError();
}
}
}
Sheep sheepA=new Sheep(“XiaoBai”,45,“White”);
Sheep sheepB=sheepA.clone();
Shallow Clone
? 方法 Object.clone()只创建当前对象的影子拷
贝,把这个过程称为 影子复制
? 影子复制 是在复制过程中只把基本数据类型
的引用类型的值复制到新的对象中
? 源对象与其影子拷贝的基本数据类型的域具
有相同的值,当引用类型的域且共同引用相
同的对象
public class IntegerStack implements Cloneable{
private int[] buffer;
private int top;
public IntegerStack(int maxSize){
buffer=new int[maxSize];
top=-1;
}
public void push(int val){
buffer[++top]=val;
}
public int pop(){
return buffer[top--];
}
public Object clone() throws CloneNotSupportedException{
return super.clone();
}
}
IntegerStack first=new IntegerStack(2);
first.push(2);
first.push(9);
IntegerStack second=(IntegerStack)first.clone();
buffer
top,1
buffer
top,1
first
second
2 9
Deep Clone
? 深度复制 是指在对象复制过程中除了所有的
基本数据类型的域将被复制,所有的引用类
型的域所引用的对象也将被复制
? 数组对象 和类库绝大部分的 集合对象 都支持
复制机制
? 如果对象包含不可变对象的域,如 String类
对象和 Wrapper类对象,则无需复制这些域
public class IntegerStack implements Cloneable{
private int[] buffer;
private int top;
public IntegerStack(int maxSize){
buffer=new int[maxSize];
top=-1;
}
public void push(int val){
buffer[++top]=val;
}
public int pop(){
return buffer[top--];
}
public Object clone() {
try{
IntegerStack copy=(IntegerStack)super.clone();
copy.buffer=(int[])buffer.clone();
return copy;
}catch(CloneNotSupportedException e) { throw new InternalError(); }
}
}
buffer
top,1
buffer
top,1
first
second
2 9
2 9
Object Serialization
? 对象序列化 (Serialization)是指将对象以字节
流的形式在网上传输,如实现远程方法调用
或保存到文件、数据库中
? 对象反序列化 (Deserialization)是指从字节流
中重构对象
? 类的对象要支持序列化和反序列化必须使用
接口 java.io.Serializable和读写对象字节流的
类 ObjectInputStream和 ObjectOutputStream
java.io.Serializable Interface
? 接口 java.io.Serializable 是一个 标记接口
? 只有实现了该接口的类的对象才能使用类
java.io.ObjectOutputStream以字节流的形式
输出,被 ObjectOutputStream输出的字节流
才能被类 java.io.ObjectInputStream读入并重
构成对象
class <类名 > implements java.io.Serializable
{ …… }
java.io.ObjectOutputStream Class
? 类 ObjectOutputStream是抽象类 OutputStream
的子类,并实现了接口 DataOutput和接口
ObjectOutput
? 该类的主要功能方法如下,
public void writeObject(Object obj)
throws IOException
? 该方法将对象的 类,类的所有 非 transient和
非 static的域 的值以及它的 所有父类 都输出到
字节流中
int[] arrayOut=new int[]{1,2,3,4,5,6};
FileOutputStream fos=new FileOutputStream(“array.ser");
ObjectOutputStream oos=new ObjectOutputStream(fos);
oos.writeObject(arrayOut);
oos.close();
java.io.ObjectInputStream Class
? 类 ObjectInputStream是抽象类 InputStream的
子类,并实现了接口 DataInput和 ObjectInput
? 该类的主要功能方法如下,
public Object readObject()
throws IOException
? 该类用于读入由 ObjectOutputStream类创建的
对象字节流并重构对象,新建的对象相当于
原对象的拷贝
FileInputStream fis=new FileInputStream("array.ser");
ObjectInputStream ois=new ObjectInputStream(fis);
int[] arrayIn=(int[])ois.readObject();
for(int i=0;i<arrayIn.length;i++)
System.out.print(arrayIn[i]+“\t”);
1 2 3 4 5 6
Default Serialization Progress
? 类 ObjectOutputStream的 writeObject()方法 把
要序列化的对象以及它的所有父类依次压入
堆栈 中,然后再对这些类进行序列化,如果
当前被序列化的对象没有 writeObject()和
readObject()方法,那么 ObjectOutputStream
的 defaultWriteObject()和 defaultReadObject()
方法将被调用,defaultWriteObject()方法将
写出对象所有非 transient和非 static的域
Default Serialization Progress
? 使用默认序列化的类要满足以下要求,
(1) 类的超类 (父类或祖先类 )必须提供无参
数的构造函数
(2) 类的所有非 transient和非 static的域都必须
支持序列化,并且类的父类或祖先类也必须
支持序列化
Default Deserialization Progress
? 类 ObjectInputStream的 readObject()方法用于
读入类序列化的字节流,如果类已被 JVM载
入,则类的静态域将保持不动,将新建对象
的所有 transient域初始化为所属类型的默认
值,再从类的一个实现了 Serializable接口的
超类开始依次读入对象的域
? 如果 JVM无法载入被反序列化的对象所属的
类,则会抛出 ClassNotFoundException异常
Customized Serialization
? 对于大多数情况,默认序列化都能满足需要,
但有时必须自定义序列化过程,
(1) 对象包含了不支持序列化的域
(2) 对象所属类的某些超类不支持序列化
(3) 集合对象中包含了不支持序列化的对象
(4) 默认序列化效率太低
import javax.swing.ImageIcon;
……
ImageIcon icon=new ImageIcon(“test.gif”);
FileOutputStream fos=new FileOutputStream(“test.ser”);
ObjectOutputStream oos=new ObjectOutputStream(fos);
oos.write(icon);
17.3KB
test.gif
236.6KB
test.ser
Customized Serialization
? 要实现自定义序列化,必须定义以下方法,
private void writeObject(ObjectOutputStream out)
throws IOException { … 自定义序列化过程 … }
? 要实现自定义反序列化,必须定义以下方法,
private Object readObject(ObjectInputStream in)
throws IOException { … 自定义反序列化过程 … }
? 在自定义反序列化不能序列化对象的 final域
public class BetterName implements Serializable{
private String name;
private long id;
private transient int hash;
private void writeObject(ObjectOutputStream out)
throws IOException {
out.writeUTF(name);
out.writeLong(id);
}
private void readObject(ObjectInputStream in)
throws IOException {
name=in.readUTF();
id=in.readLong();
hash=name.hashCode();
}
}
Object Versioning
? 每个可序列化的类都有一个 long型的 序列化版本唯
一标识符 (serial version UID),默认情况下由类的
所有非 transient和非 static的域的类型的 Class对象的
hashCode()方法返回值计算得到
? 序列化版本唯一标识符将在对象序列化过程中写
入字节流中,当对象反序列化时,将 从字节流中
读取该版本唯一标识符,并与当前类的版本唯一
标识符比较,如果不相等,则反序列化失败,抛
出 InvalidClassException异常
serialVersionUID Field
? 为了保证类的不同版本之间序列化过程的兼
容性,对于实现接口 Serializable的类都要定
义域 serialVersionUID如下,
private static final long servialVersionUID = <值 >
? 该域的值可以使用 JDK中的命令 serialver可
以计算得到
Externalizable Interface
? 接口 java.io.Externalizable是接口 Serializable
的子接口,该接口用于使程序员完全控制对
象序列化的整个过程
public interface Externalizable
extends Serializable {
void readExternal(ObjectInput in)
throws IOException;
void writeExternal(ObjectOutput out);
throws IOException;
}
Customized Serialization
and Externalizable Interface
? 默认情况下 Java对象的序列化过程是从父类
到子类顺序进行的。自定义序列化中提供的
writeObject()和 readObject()方法不能覆盖父
类的序列化过程,但是实现 Externalizable接
口就可以取得对象序列化的完全控制权,一
旦实现了该接口,这个类就要自己负责父类
的序列化过程
Assignments (作业 )
? 课本 94页 练习 3.8
? 课本 94页 练习 3.9
(将程序源码文件和编译后的字节码文件打包上传 )