您的位置:首页 > 移动开发 > Objective-C

J2SE(十八)Object之Clone

2016-05-18 16:25 483 查看
前言

        在我们编码过程中,我们有时候会遇见这种情况:有一个对象A,在某一时刻A中已经包含了一些有效值,此时可能会需要一个和A完全相同的新对象B,并且此后对B的任何改动都不能影响到A中的值,也就是说,A与B是两个独立的对象,但B的初始值是由A对象确定的。在Java语言中实现它的方法有很多,今天我们就来探讨一种简单而高效的实现方法,即通过Object的clone方法实现。

一、相关类

Java中跟克隆有关的两个类分别是Cloneable接口和Object类中的clone方法,通过两者的协作来实现克隆。Clonable接口api描述如下:

java.lang.Cloneable 接口(以下源引JavaTM 2 Platform Standard Ed. 5.0 API DOC)
此类实现了 Cloneable 接口,以指示 Object.clone() 方法可以合法地对该类实例进行按字段复制。 如果在没有实现 Cloneable 接口的实例上调用 Object 的 clone 方法,则会导致抛出 CloneNotSupportedException异常。
按照惯例,实现此接口的类应该使用公共方法重写 Object.clone(它是受保护的)。请参阅 Object.clone(),以获得有关重写此方法的详细信息。
注意,此接口不包含 clone 方法。因此,因为某个对象实现了此接口就克隆它是不可能的。即使 clone 方法是反射性调用的,也无法保证它将获得成功。       上面的意思是Cloneable接口没有任何方法,仅是个标志接口(tagging interface),若要具有克隆能力,实现Cloneable接口的类必须重写从Object继承来的clone方法,并调用Object的clone方法。

二、例子
public class Person implements Cloneable{

private int id;

private String name;

public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public Person(int id, String name) {
super();
this.id = id;
this.name = name;
}

@Override
public String toString() {
return "Person [id=" + id + ", name=" + name + "]";
}

@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}测试方法
public static void main(String[] args) throws CloneNotSupportedException {
Person p=new Person(1, "zhangsan");
Person p1=(Person) p.clone();
p1.setName("wangwu");
System.out.println(p);
System.out.println(p1);
System.out.println(p==p1);
System.out.println(p.equals(p1));
}结果集
Person [id=1, name=zhangsan]
Person [id=1, name=wangwu]
false
false从结果集可以看出,通过Person p的clone方法得到了Person p1,p与p1不相等且对p1做的修改不影响p。

三、浅克隆与深度克隆
        从上面的程序我们可以看出,clone调用了object的clone方法,创建一个对象然后一一赋值。对于原始类型跟不可变类型(String,Long...)来说是这种方法是没有问题的,但是如果是复杂类型比如list的话,那么clone对象跟原始对象会指向同一个对象。

我们将上面的代码做一定的修改,在Person对象中添加属性List<String> list

private List<String> list;

public List<String> getList() {
return list;
}

public void setList(List<String> list) {
this.list = list;
}

@Override
public String toString() {
return "Person [id=" + id + ", name=" + name + ", list=" + list + "]";
}

public Person(int id, String name, List<String> list) {
super();
this.id = id;
this.name = name;
this.list = list;
}测试代码修改为
public static void main(String[] args) throws CloneNotSupportedException {
List<String> list=new ArrayList<String>();
list.add("a");
list.add("b");
list.add("c");
Person p=new Person(1, "zhangsan",list);
Person p1=(Person) p.clone();
p1.getList().add("d");
System.out.println(p);
System.out.println(p1);
System.out.println(p==p1);
System.out.println(p.equals(p1));
}结果集
Person [id=1, name=zhangsan, list=[a, b, c, d]]
Person [id=1, name=zhangsan, list=[a, b, c, d]]
false
false        可以看出,p跟p1的List指向同一个应用,对p1的list做了修改影响到p的list值,这种情况称之为浅克隆,即被复制的对象的所有成员属性都有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。换言之,浅层复制仅仅复制所考虑的对象,而不复制它所引用的对象。那什么是深克隆呢?深克隆指的是被复制对象的所有变量都含有与原来的对象相同的值,除去那些引用其他对象的变量。那些引用其他对象的变量将指向被复制过的新对象,而不是原有的那些被引用的对象。换言之,深层复制要复制的对象引用的对象都复制一遍。
那么如何实现深克隆呢,常用的方法是采用串行化,重写Person的clone方法,代码如下

@Override
public Object clone(){
ObjectInputStream oi=null;
Object o=null;
try {
//将对象写到流里
ByteArrayOutputStream bo=new ByteArrayOutputStream();
ObjectOutputStream oo=new ObjectOutputStream(bo);
oo.writeObject(this);
//从流里读出来
ByteArrayInputStream bi=new ByteArrayInputStream(bo.toByteArray());
oi=new ObjectInputStream(bi);
o=oi.readObject();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return o;
}这样就避免了引用类型的clone问题。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  J2SE