在程序开发中,有时可能好会遇到下列情况:已经存在一个对象A,现在需要一个与对象A完全相同的B对象,并对B对象的值进行修改,但是A对象的原有的属性值不能改变。这时,如果使用java提供的对象赋值语句,当修改B对象值后,A对象的值也会被修改。那么应该如何实现创建一个和对象A完全相同的对象B,而且修改对象B时,对象A的属性值不被改变呢?
要实现这一功能,可以使用Object类中的clone方法。clone方法可以完成对象的浅克隆。所谓浅克隆就是说被克隆的对象的各个属性都是基本类型,而不是引用类型(接口、类、数组),如果存在引用类型的属性,则需要进行深克隆。
1.浅克隆
编写一个 Address类,state(国家)、province(省)、city(市),然后再构造方法中初始化这三个值,并提供 getter()和setter()方法,用于修改这三个字段,最后重写toString()来输出该类的对象,代码如下:
public class Address{
private String state;
private String province;
private String city;
public Address(String state, String province, String city) {
this.state = state;
this.province = province;
this.city = city;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
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;
}
@Override
public String toString() {
return "Address{" +
"state='" + state + '\'' +
", province='" + province + '\'' +
", city='" + city + '\'' +
'}';
}
}
编写一个类Employee,定义name、age、address,在构造方法中初始化这三个字段,同样实现getter()和setter()和 toString(),然后实现 Cloneable接口,最后重写clone方法来提供克隆的功能。
public class Employee implements Cloneable{
private String name;
private int age;
private Address address;
public Employee(String name, int age, Address address) {
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 "Employee{" +
"name='" + name + '\'' +
", age=" + age +
", address=" + address +
'}';
}
@Override
protected Employee clone(){
Employee employee = null;
try {
employee = (Employee)super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return employee;
}
}
编写 Text 类来进行测试下:
@Test
public void test(){
System.out.println("克隆之前");
Address address = new Address("中国", "河北省", "沧州市");
Employee employee = new Employee("马苏", 32, address);
System.out.println("员工 1 的信息 ==> "+ employee);
System.out.println("克隆之后");
Employee employee1 = employee.clone();
employee1.getAddress().setState("中国");
employee1.getAddress().setProvince("天津");
employee1.getAddress().setCity("北辰区");
employee1.setName("王丽坤");
employee1.setAge(33);
System.out.println("员工 2 的信息 ==> "+ employee1);
System.out.println("员工 1 的信息 ==> "+ employee);
}
结果如下图:
会发现一个问题,第一个员工对象的名字和年龄和第二个员工对象的名字和年龄不一致,说明name(基本类型)和age(基本类型)克隆成功,即修改第二个员工的名字和年龄不会影响到第一个员工的名字和年龄。但是,我修改完第二个员工的地址后发现第一个员工的地址也发生了变化,说明 Address(引用类型)克隆不成功,即修改的是同一个对象,如果想把Address(引用类型)克隆成功,需要深克隆。
深克隆
要想实现 Address的深克隆,首先让Address类实现 Cloneable 接口,重写clone方法。
public class Address implements Cloneable{
private String state;
private String province;
private String city;
public Address(String state, String province, String city) {
this.state = state;
this.province = province;
this.city = city;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
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;
}
@Override
public String toString() {
return "Address{" +
"state='" + state + '\'' +
", province='" + province + '\'' +
", city='" + city + '\'' +
'}';
}
@Override
protected Address clone(){
Address address = null;
try {
address = (Address)super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return address;
}
}
在Employee类的clone方法里添加一行代码:employee.address = address.clone();
public class Employee implements Cloneable{
private String name;
private int age;
private Address address;
public Employee(String name, int age, Address address) {
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 "Employee{" +
"name='" + name + '\'' +
", age=" + age +
", address=" + address +
'}';
}
@Override
protected Employee clone(){
Employee employee = null;
try {
employee = (Employee)super.clone();
employee.address = address.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return employee;
}
}
编写测试类
@Test
public void test(){
System.out.println("克隆之前");
Address address = new Address("中国", "河北省", "沧州市");
Employee employee = new Employee("马苏", 32, address);
System.out.println("员工 1 的信息 ==> "+ employee);
System.out.println("克隆之后");
Employee employee1 = employee.clone();
employee1.getAddress().setState("中国");
employee1.getAddress().setProvince("天津");
employee1.getAddress().setCity("北辰区");
employee1.setName("王丽坤");
employee1.setAge(33);
System.out.println("员工 2 的信息 ==> "+ employee1);
System.out.println("员工 1 的信息 ==> "+ employee);
}
这次修改第二个员工的地址不会影响第一个员工的地址,说明深克隆成功。
总结:如果需要克隆的类中只有基本类型的话,那么只在该类中实现
Cloneable接口即可,如果该类中除了包含基本类型之外,还有引用类型的话,需要将该引用对象也要实现Cloneable接口,并重写clone方法。