克隆这个词最早出现在生物学,在java中引入这个概念是为了在特定的场景避免大量的get,set方法,java的克隆有两种方式: 深克隆和浅克隆,这里分析一下这两种克隆的方式实现方式和使用场景;
浅克隆
浅克隆的实现方式是实现Cloneable接口并重写Object类中的clone()方法;
现在随便写一个类
package com.lbh.test;
public class Person implements Cloneable{
private String name; //姓名
private int age; //年龄
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 Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
@Override
public Object clone(){
Person o = null;
try{
o = (Person)super.clone();
}catch(CloneNotSupportedException e){
e.printStackTrace();
}
return o;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
}
测试克隆
package com.lbh.test;
public class TestOfClone {
public static void main(String[] args) throws Exception {
Person p1 = new Person("张山",20);
Person p2 = (Person) p1.clone();
System.out.println(p1);
System.out.println(p2);
}
}
输出:
Person [name=张山, age=20]
Person [name=张山, age=20]
至此克隆完成,但是如果Person这个类的成员变量中有对象,这种方式就行不同了
新建一个Book类
package com.lbh.test;
public class Book {
private String name; //书名
private String author; //作者
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public Book(String name, String author) {
super();
this.name = name;
this.author = author;
}
@Override
public String toString() {
return "Book [name=" + name + ", author=" + author + "]";
}
}
在person中加入一个Book类型的成员变量
private String name; //姓名
private int age; //年龄
private Book book;
再次测试
public static void main(String[] args) throws Exception {
Person p1 = new Person("张山",20,new Book("风起陇西","马伯庸"));
Person p2 = (Person) p1.clone();
p2.getBook().setName("长安十二时辰");
System.out.println(p1);
System.out.println(p2);
}
输出
Person [name=张山, age=20, book=Book [name=长安十二时辰, author=马伯庸]]
Person [name=张山, age=20, book=Book [name=长安十二时辰, author=马伯庸]]
这里可以看到,修改了p2中book的属性,p1中book的属性也会随之变化,造成这种结果的原因是浅克隆只复制了对象的引用地址,两个对象指向同一个内存地址
解决方案就是深克隆
深克隆
为了解决浅克隆中只是复制对象的引用地址,Java提供了深克隆
实现方式有多种,这里提供一种基于流的方式,
首先对应的Person类和Book实现Serializable接口
public class Person implements Cloneable,Serializable{
private static final long serialVersionUID = 8832516388417668452L;
public class Book implements Serializable{
private static final long serialVersionUID = 3405770039074127983L;
然后提供一个基于流的克隆方法
@SuppressWarnings("unchecked")
public static <T extends Serializable> T clone(T obj) throws Exception{
ByteArrayOutputStream bout = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bout);
oos.writeObject(obj);
ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bin);
return (T) ois.readObject();
}
public static void main(String[] args) throws Exception {
Person p1 = new Person("张山",20,new Book("风起陇右","马伯庸"));
Person p2 = TestOfClone.clone(p1);
p2.getBook().setName("长安十二时辰");
System.out.println(p1);
System.out.println(p2);
}
再看输出结果
Person [name=张山, age=20, book=Book [name=风起陇右, author=马伯庸]]
Person [name=张山, age=20, book=Book [name=长安十二时辰, author=马伯庸]]
这个时候会发现,修改了p2中book的属性,p1中book的属性不会变化
应用场景
开发时遇到需要克隆对象只有基本属性,不包含对象属性时可以使用浅克隆;如果包含对象属性可以使用深拷贝;