您的位置:首页 > 编程语言 > Java开发

java的clone你知道多少?

2014-01-02 22:52 253 查看
我们都知道,java提供了Cloneable接口,其意思就是对象的拷贝,我们用到这功能的时候,需实现这个接口!

那我们为什么要用它?用它能给我们带来什么好处呢?

因为拷贝是在内存中进行的,所以,比直接new对象要快,特别是在大对象的生存上(有别于数据库中的clob,特指属性比较多,字段比较复杂的对象),但是对象的拷贝也会带给我们一些迷惑,稍有不慎便会陷入这个坑中。

列子1:

public class TestClone {
public static void main(String[] args) {
Person father=new Person("父亲");
Person subPer=new Person("儿子",father);
//儿子二是通过subPer拷贝过来的
Person subPer1=subPer.clone();
subPer1.setName("儿子1");
System.out.println(subPer.getName()+"的父亲是"+subPer.getFather().getName());
System.out.println(subPer1.getName()+"的父亲是"+subPer.getFather().getName());

}

}




public class Person implements Cloneable {
//姓名
private String name="";
//父亲
   private Person father=null;
   public Person(String name){
    this.name=name;
   }
   public Person(String name,Person father){
    this.name=name;
    this.father=father;
   }
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Person getFather() {
return father;
}
public void setFather(Person father) {
this.father = father;
}
@Override
protected Person clone() {
Person p=null;
try{
p=(Person) super.clone();
}catch(CloneNotSupportedException e){
e.printStackTrace();
}

return p;
}

}


这里模拟了一个父亲有两个儿子这么一个场景,大儿子和小儿子同种,所以小儿子的对象是通过拷贝大儿子的对象来完成的,这十分正确,没什么问题的啊!但是呢?软件的开发修改时无穷尽的,就比如有那么一天,老爸突然心血来潮,突然想给大儿子找个干爹



Person father=new Person("父亲");
Person subPer=new Person("儿子",father);
//儿子1二是通过儿子拷贝过来的
Person subPer1=subPer.clone();
subPer1.setName("儿子1");
subPer.getFather().setName("大儿子的个干爹");
System.out.println(subPer.getName()+"的父亲是"+subPer.getFather().getName());
System.out.println(subPer1.getName()+"的父亲是"+subPer.getFather().getName());


其结果是 

Person father=new Person("父亲");
Person subPer=new Person("儿子",father);
//儿子1二是通过儿子拷贝过来的
Person subPer1=subPer.clone();
subPer1.setName("儿子1");
subPer.getFather().setName("大儿子的个干爹");
System.out.println(subPer.getName()+"的父亲是"+subPer.getFather().getName());
System.out.println(subPer1.getName()+"的父亲是"+subPer.getFather().getName());




其结果是:

儿子的父亲是大儿子的个干爹

儿子1的父亲是大儿子的个干爹


两儿子的老爸都变了,这个老爹从人间蒸发,妈妈啊!

出现这种现象的原因是什么呢?

我们都知道,java中所有的类都继承Object,Object提供了一个默认的拷贝方法,即super.clone(),但是,该方法是有缺陷的,它提供的是一种浅拷贝,它不把对象的所有属性都拷贝一份,它支持基本数据类型 比如int,float,double,如果变量是一个实例对象,则拷贝地址引用,也就是说此时新拷贝出来的对象与原有的对象共享该实例变量,不受访问权限的限制。我的妈妈啊!这不是违背了java
中private 只能在一个类中使用的原则么,这让java 的private 情何以堪!!!!


 也许你也发现了,String不也是引用类型的么?不也是能正常拷贝么,呵呵,这就得说说String 这个玩意,这个玩意比较特殊,特殊在什么地方呢?其实clone 的时候也是拷贝的地址,但是呢,修改的时候会从字符串池中重新生成新的字符串,原有的字符串保存不变,其实这么一说也就明白了 原来儿子一是通过大儿子产生的,我的乖乖!!那怎么修改呢?




protected Person clone() {
Person p=null;
try{
p=(Person) super.clone();
p.setFather(new Person(p.getFather().getName()));
}catch(CloneNotSupportedException e){
e.printStackTrace();
}

return p;
}


这就是对引用类型的拷贝,聪明的你或许已经发现,其实对引用的拷贝是在什么地方修改的,那我们来看看集合的拷贝!

列2:对集合的拷贝

public class Person implements Cloneable{
// 姓名
private String name;
// 年龄
private int age;
// 性别
private String sex;
//朋友
private List<String> friends;

public List<String> getFriends() {
return friends;
}

public void setFriends(List<String> friends) {
this.friends = friends;
}

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 String getSex() {
return sex;
}

public void setSex(String sex) {
this.sex = sex;
}

public Person clone() {
try {
Person person  = (Person)super.clone();
List<String> newfriends = new ArrayList<String>();
for(String friend : this.getFriends()) {
newfriends.add(friend);
}
person.setFriends(newfriends);
return  person;
} catch (CloneNotSupportedException e) {
e.printStackTrace();
return null;
}
}

}


ok,我们已经知道了拷贝的使用,但是呢?你想过没,如果项目中有大量的对象是通过拷贝来生存的,那我们该如何处理呢?一种选择方式是通过序列化方式来处理,何为序列化?就是对象可以通过介质传输,列如内存,然后通过反序列化可以得到原始的对象,

public class CloneUtils {
public CloneUtils() {
throw new Error("工具类不能创建对象!");
}

@SuppressWarnings("unchecked")
public static <T extends Serializable> T clone(T obj) {
// T 拷贝产生的对象
T cloneObj = null;
try {
// 读取对象字节流
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(obj);
oos.close();
// 分配内存空间,写入原始对象,生存新对象
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
cloneObj = (T) ois.readObject();
ois.close();
} catch (Exception e) {
}
return cloneObj;
}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: