拷贝
为什么拷贝要实现Cloneable接口?
对象使用拷贝方法clone()是Object里面的方法,但是要在类上实现Cloneable接口。
该步骤其实和Serializable一样,本身是没有任何方法实现的,只是为了在执行的时候给JVM一个标志,告诉其实现该接口,从而能复写clone()方法。
public class User
public class User implements Cloneable
public class LearnClone {
public static void main(String[] args) throws CloneNotSupportedException {
Object clone = new User("wzh").clone();
}
}
从反编译看,有无实现Cloneable都是一样的步骤。下图第一个是没有实现Cloneable,第二次是实现Cloneable的。
没有实现Cloneable就去重写Object的clone方法,这是可以的。但是,在真正使用的时候,就会报CloneNotSupportedException。
public class User {
private String name;
public User(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
从Object看:
protected native Object clone() throws CloneNotSupportedException;
这个步骤和使用序列化的时候,实现Serializable接口是一个道理,只是为了让JVM进行识别。
常见拷贝方式
浅拷贝
复制例子
public class ShallowCloneExample implements Cloneable {
private int[] arr;
public ShallowCloneExample() {
arr = new int[10];
for (int i = 0; i < arr.length; i++) {
arr[i] = i;
}
}
public void set(int index, int value) {
arr[index] = value;
}
public int get(int index) {
return arr[index];
}
//浅拷贝的实现
@Override
protected ShallowCloneExample clone() throws CloneNotSupportedException
{
return (ShallowCloneExample) super.clone();
}
}
ShallowCloneExample e1 = new ShallowCloneExample();
ShallowCloneExample e2 = null;
try {
e2 = e1.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
e1.set(2, 222);
System.out.println(e2.get(2)); // 22
详细讲解
拷⻉对象和原始对象的引⽤类型引⽤同⼀个对象,如果对象中有基本类型数值,直接复制。如果对象中有引用类型,其只是拷贝其引用类型的地址。
public class LearnClone {
public static void main(String[] args) throws CloneNotSupportedException {
User user = new User("wzh", new Person(18));
User clone = (User)user.clone();
//true
System.out.println(user.getPerson().toString() == clone.getPerson().toString());
//false
System.out.println(user.toString() == clone.toString());
}
}
public class User implements Cloneable{
private String name;
private Person person;
public Person getPerson() {
return person;
}
public void setPerson(Person person) {
this.person = person;
}
public User(String name, Person person) {
this.name = name;
this.person = person;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class Person {
private Integer age;
public Person(Integer age) {
this.age = age;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
深拷贝
拷⻉对象和原始对象的引⽤类型引⽤不同对象。
复制例子
public class DeepCloneExample implements Cloneable {
private int[] arr;
public DeepCloneExample() {
arr = new int[10];
for (int i = 0; i < arr.length; i++) {
arr[i] = i;
}
}
public void set(int index, int value) {
arr[index] = value;
}
public int get(int index) {
return arr[index];
}
//使用深拷贝,需要对引用类型字段逐一处理,过程比较繁琐
@Override
protected DeepCloneExample clone() throws CloneNotSupportedException {
//必不可少的步骤,调用Object.clone()方法。
DeepCloneExample result = (DeepCloneExample) super.clone();
result.arr = new int[arr.length];
for (int i = 0; i < arr.length; i++) {
result.arr[i] = arr[i];
}
return result;
}
}
DeepCloneExample e1 = new DeepCloneExample();
DeepCloneExample e2 = null;
try {
e2 = e1.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
e1.set(2, 222);
System.out.println(e2.get(2)); // 2
clone()的替代方案
使⽤ clone() ⽅法来拷⻉⼀个对象即复杂⼜有⻛险,它会抛出异常,并且还需要类型转换。Effective Java 书上讲到,最好不要去使⽤ clone(),可以使⽤拷⻉构造函数或者拷⻉⼯⼚来拷⻉⼀个对象。
public class CloneConstructorExample {
private int[] arr;
public CloneConstructorExample() {
arr = new int[10];
for (int i = 0; i < arr.length; i++) {
arr[i] = i;
}
}
public CloneConstructorExample(CloneConstructorExample original) {
arr = new int[original.arr.length];
for (int i = 0; i < original.arr.length; i++) {
arr[i] = original.arr[i];
}
}
public void set(int index, int value) {
arr[index] = value;
}
public int get(int index) {
return arr[index];
}
}
CloneConstructorExample e1 = new CloneConstructorExample();
CloneConstructorExample e2 = new CloneConstructorExample(e1);
e1.set(2, 222);
System.out.println(e2.get(2)); // 2