Java基础——克隆
1、克隆
假设有一个对象object1,在某处又需要一个跟object1一样的实例object2,这两个对象是绝对独立的,不会因为某一个修改另一个随之改变,这样,我们不能直接将对象objec1t的引用赋予object2,object2=object, 这样两个引用仍然指向的是同一个对象,不是两个对象,所以就需要将object1的属性克隆到object2身上
Java中克隆分为两种,浅克隆和深克隆
2、浅克隆
把原型对象中成员变量为值类型的属性都复制给克隆对象,把原型对象中成员变量为引用类型的引用地址也复制给克隆对象
会复制原型对象,但不会复制它所引用的对象
浅克隆只是复制了对象的引用地址,两个对象指向同一个内存地址,所以修改其中任意的值,另一个值都会随之变化
Object类中的clone()方法就是浅克隆
/** * Creates and returns a copy of this object. The precise meaning * of "copy" may depend on the class of the object. The general * intent is that, for any object {@code x}, the expression: * <blockquote> * <pre> * x.clone() != x</pre></blockquote> * will be true, and that the expression: * <blockquote> * <pre> * x.clone().getClass() == x.getClass()</pre></blockquote> * will be {@code true}, but these are not absolute requirements. * While it is typically the case that: * <blockquote> * <pre> * x.clone().equals(x)</pre></blockquote> * will be {@code true}, this is not an absolute requirement. * <p> * By convention, the returned object should be obtained by calling * {@code super.clone}. If a class and all of its superclasses (except * {@code Object}) obey this convention, it will be the case that * {@code x.clone().getClass() == x.getClass()}. * <p> * By convention, the object returned by this method should be independent * of this object (which is being cloned). To achieve this independence, * it may be necessary to modify one or more fields of the object returned * by {@code super.clone} before returning it. Typically, this means * copying any mutable objects that comprise the internal "deep structure" * of the object being cloned and replacing the references to these * objects with references to the copies. If a class contains only * primitive fields or references to immutable objects, then it is usually * the case that no fields in the object returned by {@code super.clone} * need to be modified. * <p> * ...... */ //Object类中的clone()方法 protected native Object clone() throws CloneNotSupportedException;
3、深克隆
将原型对象中的所有类型,无论是值类型还是引用类型,都复制一份给克隆对象,也就是说深克隆会把原型对象和原型对象所引用的对象,都复制一份给克隆对象
深克隆是将对象及值复制过来,两个对象修改其中任意的值另一个值不会改变
实现Cloneable接口并重写Object类中的clone()方法或者实现Serializable接口,通过对对象的序列化、反序列化实现克隆和字节流输出,通过这三种方式可以实现深克隆
Cloneable接口没有任何方法,仅是个标志接口(tagging interface),若要具有克隆能力,实现Cloneable接口的类必须重写从Object继承来的clone方法,并调用Object的clone方法(见下面Object#clone的定义),重写后的方法应为public的
//Cloneable 接口源码
public interface Cloneable { } //重写clone()方法 class CloneClass implements Cloneable{ public Object clone(){ CloneClass o = null; try{ o = (CloneClass)super.clone();
CloneClass .setAddress(this.address.clone());//引用克隆类型赋值
}catch(CloneNotSupportedException e){
e.printStackTrace();
}
return o;
}
}
如果引用类型中海包括引用类型, 要实现多层克隆会很麻烦, 这使用可以使用序列化和反序列化的方式实现对象的深克隆,把对象写到字节流中的过程是序列化的过程, 而把对象从字节流中读出来的过程是反序列化的过程. 由于Java序列化的过程中, 写在流中的是对象的一个拷贝, 而原对象仍然在JVM中
public class A implements Serializable { private static final long serialVersionUID = "序列化ID"; //显式声明ID public Outer Aclone() { Outer outer = null; try { // 将对象序列化成流,因为写在流里的是对象的一个拷贝,而原对象仍然存在于JVM里面 ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(this); // 将流序列化成对象 ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bais); outer = (Outer) ois.readObject(); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } return outer; } }
通过字节流实现深克隆:
/** * 通过字节流实现克隆 */ static class StreamClone { public static <T extends Serializable> T clone(People obj) { T cloneObj = null; try { // 写入字节流 ByteArrayOutputStream bo = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bo); oos.writeObject(obj); oos.close(); // 分配内存,写入原始对象,生成新对象 ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());//获取上面的输出字节流 ObjectInputStream oi = new ObjectInputStream(bi); // 返回生成的新对象 cloneObj = (T) oi.readObject(); oi.close(); } catch (Exception e) { e.printStackTrace(); } return cloneObj; } }