本文首发于 cartoon 的博客
转载请注明出处:https://cartoonyu.github.io/cartoon-blog/post/java/java%E5%AE%9E%E7%8E%B0%E5%85%8B%E9%9A%86%E7%9A%84%E6%96%B9%E6%B3%95/
前言
这也是昨天的面试题。
当时只说了深拷贝以及浅拷贝,面试官问了两遍还有吗,我很肯定的说就这两种了,面试结束之后查了一下,啪啪打脸。
正文
JAVA 实现克隆有两种形式
- 浅克隆
- 深克隆
浅克隆与深克隆的区别
JAVA 将数据类型分为基本数据类型以及引用数据类型,我认为浅克隆与深克隆的区别主要在于对引用类型的成员属性的操作。深度克隆应该递归克隆引用类型的成员属性。
浅克隆实现
- 实现 Cloneable 接口
- 重写 clone 方法,调用父类的 clone 方法
代码
public class Text implements Cloneable{
private int age;
private Name name;
public int getAge() {return age;}
public void setAge(int age) {this.age = age;}
public Name getName() {return name;}
public void setName(Name name) {this.name = name;}
@Override
protected Object clone(){
try {return super.clone();
} catch (CloneNotSupportedException e) {e.printStackTrace();
}
return null;
}
}
class Name{
private String name;
public String getName() {return name;}
public void setName(String name) {this.name = name;}
}
public class Main {
public static void main(String[] args){Name name1=new Name();
name1.setName("name1");
Text t1=new Text();
t1.setAge(12);
t1.setName(name1);
Text t2=(Text) t1.clone();
System.out.println(t2.getName().getName());
name1.setName("name2");
System.out.println(t2.getName().getName());
}
}
输出
name1
name2
结果分析
因为只是直接调用父类的 clone 方法,没有对成员属性进行处理,所以在修改 t1 属性 name 的值时,t2 属性 name 的值也会随之改变。
优点
简单易实现
缺点
无法真正克隆对象
深克隆实现
通过递归克隆实现
代码
public class Text implements Cloneable{
private int age;
private Name name;
public int getAge() {return age;}
public void setAge(int age) {this.age = age;}
public Name getName() {return name;}
public void setName(Name name) {this.name = name;}
@Override
protected Object clone(){
Text text=null;
try {text=(Text) super.clone();} catch (CloneNotSupportedException e) {e.printStackTrace();
}
text.setName((Name) text.getName().clone());
return text;
}
}
class Name implements Cloneable{
private String name;
public String getName() {return name;}
public void setName(String name) {this.name = name;}
@Override
protected Object clone() {
try {return super.clone();
} catch (CloneNotSupportedException e) {e.printStackTrace();
}
return null;
}
}
输出
name1
name1
通过序列化实现
代码
public class Text implements Serializable{
private static final long serialVersionUID = 8723901148964L;
private int age;
private Name name;
public int getAge() {return age;}
public void setAge(int age) {this.age = age;}
public Name getName() {return name;}
public void setName(Name name) {this.name = name;}
public Object myClone(){
Text text=null;
ByteArrayOutputStream bos=new ByteArrayOutputStream();
try {ObjectOutputStream oos=new ObjectOutputStream(bos);
oos.writeObject(this);
ByteArrayInputStream bis=new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois=new ObjectInputStream(bis);
text=(Text)ois.readObject();} catch (IOException e) {e.printStackTrace();
} catch (ClassNotFoundException e) {e.printStackTrace();
}
return text;
}
}
class Name implements Serializable {
private static final long serialVersionUID = 872390113109L;
private String name;
public String getName() {return name;}
public void setName(String name) {this.name = name;}
@Override
public String toString() {return name;}
}
输出
name1
name1
结果分析
采用深克隆能有效隔离源对象与克隆对象的联系。
从实现过程来说,递归克隆存在克隆过程多且复杂的缺点,所以建议采用序列化的方式进行
深克隆。
总结
JAVA 对象克隆共有两种形式,三种方法
-
浅克隆
- 调用 clone 方法
-
深克隆
- 递归调用 clone 方法
- 序列化对象
三种方法之间互有优缺点,具体采用要根据实际情况。