new
出来的对象中的属性都是初始化时候的值,当需要一个新的对象来保存当前对象的“状态”就靠clone
方法了。直接使用Object a=new Object();Object b;b=a
不行吗,答案是不行的。因为这只是克隆了引用。
首先由两种克隆的方式,一个是深克隆,一个是浅克隆。他们之间的区别在于:是否支持引用类型的成员变量的复制,深克隆会克隆引用类型的成员变量。
clone
方法来源于Object
,我们追本逐源看看:
protected native Object clone() throws CloneNotSupportedException;
可以看出,clone()
是Object
的native
方法,而且还是protected
以为着不能再类外进行访问,想要实现克隆方法,只有覆盖这个方法。
public class Student implements Cloneable {
private String name;
private int age;
@Override
protected Object clone() {
Student student=null;
try {
student =(Student) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return student;
}
...
getter and setter
...
public class Test {
public static void main(String[] args){
Student stu1 = new Student("张三",18);
Student stu2 = (Student) stu1.clone();
stu2.setName("李四");
stu2.setAge(20);
System.out.println("stu1:"+stu1.getName()+" age:"+stu1.getAge());
System.out.println("stu2:"+stu2.getName()+" age:"+stu2.getAge());
System.out.println(stu2 == stu1); //false
}
}
stu1:张三 age:18
stu2:李四 age:20
false
上面的案例,属于浅克隆,克隆对象中的属性不包括引用对象,如果包含引用对象,则该引用对象也必须要实现cloneable接口。
public class Student implements Cloneable {
private String name;
private int age;
private Address address; //新增地址对象
@Override
protected Student clone() {
Student stu =null;
try {
stu = (Student) super.clone();
stu.setAddress(address.clone());
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return stu;
}
....
public class Address implements Cloneable {
private String add;
//也需要实现clone方法
@Override
protected Address clone() throws CloneNotSupportedException {
return (Address)super.clone();
}
....
}
public class Test {
public static void main(String[] args){
Student stu1 = new Student("张三",18,new Address("上海"));
Student stu2 = (Student) stu1.clone();
stu2.setName("李四");
stu2.setAge(20);
stu1.setAddress(new Address("北京"));
System.out.println("stu1:"+stu1.getName()+" age:"+stu1.getAge()+"address"+stu1.getAddress().getAdd());
System.out.println("stu2:"+stu2.getName()+" age:"+stu2.getAge()+"address"+stu2.getAddress().getAdd());
}
}
stu1:张三 age:18address北京
stu2:李四 age:20address上海
如果引用类型里面还包含很多引用类型,或者内层引用类型的类里面又包含引用类型,使用clone方法就会很麻烦。这时我们可以用序列化的方式来实现对象的深克隆。
public class Student implements Serializable {
private static final long serialVersionUID = -3950409490183038226L;
private String name;
private int age;
private Address address;
public Student CloneObject(Object obj){
Student student = null;
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(obj);
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
student = (Student) ois.readObject();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return student;
}
......
public class Address implements Serializable {
private static final long serialVersionUID = -4265312082191071263L;
private String add;
@Override
protected Address clone() throws CloneNotSupportedException {
return (Address)super.clone();
}
.....
}
public class Test {
public static void main(String[] args){
Student stu1 = new Student("张三",18,new Address("上海"));
Student stu2 = stu1.CloneObject(stu1);
stu2.setName("李四");
stu2.setAge(20);
stu1.setAddress(new Address("北京"));
System.out.println("stu1:"+stu1.getName()+" age:"+stu1.getAge()+"address"+stu1.getAddress().getAdd());
System.out.println("stu2:"+stu2.getName()+" age:"+stu2.getAge()+"address"+stu2.getAddress().getAdd());
}
}
stu1:张三 age:18address北京
stu2:李四 age:20address上海
总结:
实现对象克隆有两种方式:
1). 实现Cloneable接口并重写Object类中的clone()方法;
2). 实现Serializable接口,通过对象的序列化和反序列化实现克隆,可以实现真正的深度克隆.
注意:
基于序列化和反序列化实现的克隆不仅仅是深度克隆,更重要的是通过泛型限定,可以检查出要克隆的对象是否支持序列化,这项检查是编译器完成的,不是在运行时抛出异常,这种是方案明显优于使用Object类的clone方法克隆对象。让问题在编译的时候暴露出来总是优于把问题留到运行时。