Java对象克隆
1. 介绍
克隆就是复制一个对象的副本。Java支持我们对一个对象进行克隆,通常用在装饰模式和原型模式中。
一个对象中可能有基本数据类型(如:int、long、double等)的属性,也同时含有引用数据类型(如数组、集合等)的属性,所以在对象克隆时分为浅克隆与深克隆两种。那么,什么是浅克隆,什么又是深克隆呢?
浅克隆是指克隆对象时仅仅克隆对象本身(包括对象中的基本类型的变量),而不克隆对象包含的引用指向的子对象,即克隆对象与被克隆对象共享子对象。
深克隆不仅克隆对象本身,而且克隆对象包含的引用指向的所有子对象。
2. clone()方法
Object类中的clone()方法可以实现将对象复制一份并返回给调用者。一般而言,clone()方法满足:
- 对任何的对象obj,都有
obj.clone() != obj
,即克隆对象与原对象不是同一个对象。 - 对任何的对象obj,都有
obj.clone().getClass() == obj.getClass()
,即克隆对象与原对象的类型一样。 - 如果对象obj的equals()方法定义恰当,那么
obj.clone().equals(obj)
应该成立。
Object类的clone()方法执行特定的复制操作,如果此对象的类不能实现接口Cloneable,则会抛出CloneNotSupportedException异常。
在Java中,为了获取对象的一份拷贝,我们可以重写Object类的clone()方法,一般遵循下列步骤:
- 在类中实现Cloneable接口。
- 在类中覆盖基类的clone()方法,并声明为public。
- 在派生类的clone()方法中,调用super.clone()
3. 浅克隆
示例:
地址类:
package test.clone;
/**
* 地址类
*
* @author 小明
*
*/
public class Address implements Cloneable {
private String province; // 省
private String city; // 市
private String detail; // 详细地址
public Address() {
super();
}
public Address(String province, String city, String detail) {
super();
this.province = province;
this.city = city;
this.detail = detail;
}
public String getProvince() {
return province;
}
public void setProvince(String province) {
this.province = province;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getDetail() {
return detail;
}
public void setDetail(String detail) {
this.detail = detail;
}
@Override
public String toString() {
return "Address [province=" + province + ", city=" + city + ", detail="
+ detail + "]";
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
学生类:
package test.clone;
/**
* 学生类
*
* @author 小明
*
*/
public class Student implements Cloneable {
private String name; // 姓名
private int age; // 年龄
private Address address; // 地址
public Student() {
super();
}
public Student(String name, int age, Address address) {
super();
this.name = name;
this.age = age;
this.address = address;
}
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 Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + ", address="
+ address + "]";
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
测试类:
package test.clone;
/**
* 浅克隆测试
*
* @author 小明
*
*/
public class ShallowCloneTest {
public static void main(String[] args) throws CloneNotSupportedException {
Address address = new Address("广东", "广州", "天河"); // 地址
Student stu1 = new Student("小明", 28, address); // 第一个学生对象
Student stu2 = (Student) stu1.clone(); // 第二个学生对象,为克隆第一个学生对象
// 修改第二个学生对象的信息
stu2.setName("王飞鸿");
stu2.setAge(35);
stu2.getAddress().setProvince("四川");
stu2.getAddress().setCity("成都");
stu2.getAddress().setDetail("青羊区");
// 打印第一个学生对象的信息
System.out.println(stu1);
}
}
运行结果:
Student [name=小明, age=28, address=Address [province=四川, city=成都, detail=青羊区]]
从运行结果看到,我们仅只修改了第二个学生对象的地址,但第一个学生对象的地址也改变了,这说明两个学生对象是共享的学生地址对象。
那如何实现深克隆呢?
4. 深克隆
地址类:
package test.clone;
/**
* 地址类
*
* @author 小明
*
*/
public class Address implements Cloneable {
private String province; // 省
private String city; // 市
private String detail; // 详细地址
public Address() {
super();
}
public Address(String province, String city, String detail) {
super();
this.province = province;
this.city = city;
this.detail = detail;
}
public String getProvince() {
return province;
}
public void setProvince(String province) {
this.province = province;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getDetail() {
return detail;
}
public void setDetail(String detail) {
this.detail = detail;
}
@Override
public String toString() {
return "Address [province=" + province + ", city=" + city + ", detail="
+ detail + "]";
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
学生类:
package test.clone;
/**
* 学生类
*
* @author 小明
*
*/
public class Student implements Cloneable {
private String name; // 姓名
private int age; // 年龄
private Address address; // 地址
public Student() {
super();
}
public Student(String name, int age, Address address) {
super();
this.name = name;
this.age = age;
this.address = address;
}
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 Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + ", address="
+ address + "]";
}
@Override
public Object clone() throws CloneNotSupportedException {
Student stu = (Student) super.clone();
stu.address = (Address) address.clone();
return stu;
}
}
测试类:
package test.clone;
/**
* 深克隆测试
*
* @author 小明
*
*/
public class DeepCloneTest {
public static void main(String[] args) throws CloneNotSupportedException {
Address address = new Address("广东", "广州", "天河"); // 地址
Student stu1 = new Student("小明", 28, address); // 第一个学生对象
Student stu2 = (Student) stu1.clone(); // 第二个学生对象,为克隆第一个学生对象
// 修改第二个学生对象的信息
stu2.setName("王飞鸿");
stu2.setAge(35);
stu2.getAddress().setProvince("四川");
stu2.getAddress().setCity("成都");
stu2.getAddress().setDetail("青羊区");
// 打印第一个学生对象的信息
System.out.println(stu1);
// 打印第二个学生对象的信息
System.out.println(stu2);
}
}
运行结果:
Student [name=小明, age=28, address=Address [province=广东, city=广州, detail=天河]]
Student [name=王飞鸿, age=35, address=Address [province=四川, city=成都, detail=青羊区]]
从运行结果中看出,修改第二个学生对象时,第一个学生对象的地址并不受影响,说明克隆学生对象时,不仅克隆了学生对象,也克隆了其子对象。
5. 利用串行化实现深克隆
地址类:
package test.clone2;
import java.io.Serializable;
/**
* 地址类
*
* @author 小明
*
*/
public class Address implements Serializable {
private String province; // 省
private String city; // 市
private String detail; // 详细地址
public Address() {
super();
}
public Address(String province, String city, String detail) {
super();
this.province = province;
this.city = city;
this.detail = detail;
}
public String getProvince() {
return province;
}
public void setProvince(String province) {
this.province = province;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getDetail() {
return detail;
}
public void setDetail(String detail) {
this.detail = detail;
}
@Override
public String toString() {
return "Address [province=" + province + ", city=" + city + ", detail="
+ detail + "]";
}
}
学生类:
package test.clone2;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
/**
* 学生类
*
* @author 小明
*
*/
public class Student implements Serializable {
private String name; // 姓名
private int age; // 年龄
private Address address; // 地址
public Student() {
super();
}
public Student(String name, int age, Address address) {
super();
this.name = name;
this.age = age;
this.address = address;
}
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 Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + ", address="
+ address + "]";
}
/**
* 利用串行化深克隆
*
* @return 深克隆后的对象
*/
public Object deepClone() {
ObjectOutputStream oos = null;
ObjectInputStream ois = null;
try {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
oos = new ObjectOutputStream(byteArrayOutputStream);
oos.writeObject(this);
ois = new ObjectInputStream(new ByteArrayInputStream(
byteArrayOutputStream.toByteArray()));
return ois.readObject();
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
try {
if (oos != null)
oos.close();
if (ois != null)
ois.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
测试类:
package test.clone2;
/**
* 深克隆测试
*
* @author 小明
*
*/
public class DeepCloneTest {
public static void main(String[] args) throws CloneNotSupportedException {
Address address = new Address("广东", "广州", "天河"); // 地址
Student stu1 = new Student("小明", 28, address); // 第一个学生对象
Student stu2 = (Student) stu1.deepClone(); // 第二个学生对象,为克隆第一个学生对象
// 修改第二个学生对象的信息
stu2.setName("王飞鸿");
stu2.setAge(35);
stu2.getAddress().setProvince("四川");
stu2.getAddress().setCity("成都");
stu2.getAddress().setDetail("青羊区");
// 打印第一个学生对象的信息
System.out.println(stu1);
// 打印第二个学生对象的信息
System.out.println(stu2);
}
}
运行结果:
Student [name=小明, age=28, address=Address [province=广东, city=广州, detail=天河]]
Student [name=王飞鸿, age=35, address=Address [province=四川, city=成都, detail=青羊区]]
说明:利用串行化实现深克隆,克隆对象与子对象对应的类都必须实现java.io.Serializable接口。