克隆:clone
-
- 复制(实际的数据)
-
- 可以通过实现Cloneable接口和重写Object类中的clone()方法来实现对象的克隆。具体步骤如下:
-
- 除了上述两种方法,Java还提供了另一种克隆方式,即使用Object类中的clone()方法进行克隆。
-
- 通过第三方工具类Gson实现深克隆
1. 复制(实际的数据)
1
Person p1 = new Person();
Person p2 = p1;
2
Person p1 = new Person(“wang”, 20);
Person p2 = new Person();
p2.setName(p1.getName());
p2.setAge(p2.getAge());
介绍
- clone()定义在Object
- 克隆指的是指定义一个方法,“创建”与本对象“属性”一致的新对象!
- 目的:快速地“复制”对象!
- 复制的方式是,重写clone方法
- 被复制的对象类型要实现Cloneable接口
- Object类中定义的clone方法,默认地对实例变量操作的方式:“基本数据类型”复制,引用类型复制。
- 带来的结果是,引用类型的实例变量指向的对象,没有复制!
- Object clone()这种没有复制实例变量指向对象的方式,我们称之为"浅克隆"
- 如果需要“深克隆”,赋值对象,以及对象实例变量指向的所有对象!需要额外地操作。
- 额外操作方式1: 重写clone(),把需要的逻辑,在clone()方法中自己实现!
- 额外操作方式2:可以把需要克隆的对象,转化成json格式的数据。再通过json格式创建对象!
2. 可以通过实现Cloneable接口和重写Object类中的clone()方法来实现对象的克隆。具体步骤如下:
-
实现Cloneable接口,并重写Object类中的clone()方法,以便实现克隆操作。Cloneable接口是一个标记接口,用于告诉Java虚拟机该类支持克隆操作。
-
在重写的clone()方法中,调用super.clone()方法获取到一个浅克隆的副本,然后根据需要对副本进行深克隆,最后返回克隆后的对象。
示例:
public class Person implements Cloneable {
private String name;
private int age;
private List<String> hobbies;
public Person(String name, int age, List<String> hobbies) {
this.name = name;
this.age = age;
this.hobbies = hobbies;
}
// 重写clone()方法
@Override
public Object clone() throws CloneNotSupportedException {
Person clone = (Person) super.clone();
// 对hobbies进行深克隆
clone.hobbies = new ArrayList<>(this.hobbies);
return clone;
}
// 省略getter和setter方法
}
在上面的示例中,Person类实现了Cloneable接口,并重写了Object类中的clone()方法。在重写的clone()方法中,首先调用了super.clone()方法获取到一个浅克隆的副本,然后对hobbies字段进行了深克隆,最后返回克隆后的对象。
使用该类进行克隆操作的示例如下:
List<String> hobbies = new ArrayList<>();
hobbies.add("reading");
Person p1 = new Person("Tom", 20, hobbies);
Person p2 = null;
try {
p2 = (Person) p1.clone(); // 克隆p1对象
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
System.out.println("p1 == p2 ? " + (p1 == p2)); // false
System.out.println("p1.hobbies == p2.hobbies ? " + (p1.getHobbies() == p2.getHobbies())); // false
在上面的示例中,首先创建了一个Person对象p1,并将其hobbies字段设置为一个包含一个元素的List。然后使用p1.clone()方法创建了一个克隆对象p2,并判断p1和p2是否相等,以及p1和p2的hobbies字段是否相等。可以看到,p1和p2不相等,而且p1和p2的hobbies字段也不相等,这说明p1和p2是两个不同的对象,且p2的hobbies字段进行了深克隆。
除了上面的示例外,下面再给出一个深克隆的示例,使用的是序列化和反序列化的方法:
import java.io.*;
public class Person implements Serializable {
private String name;
private int age;
private Address address;
public Person(String name, int age, Address address) {
this.name = name;
this.age = age;
this.address = address;
}
public Person clone() {
try {
// 序列化
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
// 反序列化
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return (Person) ois.readObject();
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
return null;
}
}
// 省略getter和setter方法
}
class Address implements Serializable {
private String city;
private String street;
public Address(String city, String street) {
this.city = city;
this.street = street;
}
// 省略getter和setter方法
}
在上面的示例中,Person类实现了Serializable接口,并提供了一个clone()方法,该方法使用了序列化和反序列化的方法进行深克隆。Address类同样实现了Serializable接口,因为Person类中包含了Address类的实例。
使用该类进行克隆操作的示例如下:
Address address = new Address("Shanghai", "Nanjing Road");
Person p1 = new Person("Tom", 20, address);
Person p2 = p1.clone(); // 克隆p1对象
System.out.println("p1 == p2 ? " + (p1 == p2)); // false
System.out.println("p1.address == p2.address ? " + (p1.getAddress() == p2.getAddress())); // false
在上面的示例中,首先创建了一个Person对象p1,并将其address字段设置为一个Address对象。然后使用p1.clone()方法创建了一个克隆对象p2,并判断p1和p2是否相等,以及p1和p2的address字段是否相等。可以看到,p1和p2不相等,而且p1和p2的address字段也不相等,这说明p1和p2是两个不同的对象,且p2的address字段进行了深克隆。
3. 除了上述两种方法,Java还提供了另一种克隆方式,即使用Object类中的clone()方法进行克隆。
该方法会创建一个新的对象,并将原对象的非静态字段复制到新对象中。具体来说,需要满足以下条件:
被克隆的类必须实现Cloneable接口,否则会抛出CloneNotSupportedException异常;
克隆方法需要重写Object类中的clone()方法,并将其访问修饰符改为public;
克隆方法需要调用super.clone()方法获得一个新的对象,并将原对象的非静态字段复制到新对象中。
以下是一个使用clone()方法进行浅克隆的示例:
public class Person implements Cloneable {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public Person clone() {
try {
return (Person) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
return null;
}
}
// 省略getter和setter方法
}
在上述示例中,Person类实现了Cloneable接口,并重写了Object类中的clone()方法,以便进行浅克隆操作。使用该类进行克隆操作的示例如下:
Person p1 = new Person("Tom", 20);
Person p2 = p1.clone(); // 克隆p1对象
System.out.println("p1 == p2 ? " + (p1 == p2)); // false
在上述示例中,首先创建了一个Person对象p1,并使用p1.clone()方法创建了一个克隆对象p2。然后判断p1和p2是否相等,可以看到,它们不相等,说明克隆操作成功。
需要注意的是,使用Object类中的clone()方法进行克隆操作时,如果被克隆的类包含了其他类的引用类型字段,那么克隆出来的对象和原对象将共享这些引用类型字段的实例,即进行的是浅克隆操作。如果需要进行深克隆操作,则需要在克隆方法中对这些引用类型字段进行深克隆操作。
4. 通过第三方工具类Gson实现深克隆
User user1 = new User()
Gson gson = new Gson();
//将对象序列化为json字符串
String userStr = gson.toJson(user1);
//然后将字符串反序列化为对象
User user2= gson.fromJson(userStr, User.class);
这种方式不用实现Cloneable接口和Serializable接口,也不用重写clone(),对结果没影响。