文章目录
clone的方式
什么叫做克隆
就是在原有的事物的基础上去复制出来一个完全一模一样的事物,
在java中对象就是映射的现实生活,我们现在要在现有的一个对象的基础上,弄出来一个完全一样的对象,在java中的Object类中就有这样的一个方法clone(),现在有两种"克隆"方式
浅拷贝
什么叫浅拷贝,就是类似下面的代码
A a = new A();
A a2 = a;
现在我们打印a与a2会发现属性值完全一样
但是我们发现一个问题,就是当我们改变a对象的属性的值的时候,a2对象也会发生变化
这就是因为我们拷贝对象a2的时候
并没有给a2新开辟一块内存,而是将a2的指针指向了a的那块内存
所以当我们打印的时候,a与a2的结果肯定是一样的,因为就是同一块内存嘛
所以当我们改变a的值,a2也会变化,这就是浅拷贝
深拷贝
深拷贝就与浅拷贝不同,它会给a2去新开辟一块内存,将a的内存里所有的值全部复制过来
这样a与a2的值在外界看来是一模一样的,当我们改变其中一个时,另一个也不会随之改变
通过重写Object类的clone方法
类A
public class A implements Serializable,Cloneable{
private int age = 0;
private String name = "张三";
private Double price = 2.5;
private B b = new B();
@Override
protected Object clone() throws CloneNotSupportedException {
A cloneA = (A) super.clone();
return cloneA;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
public B getB() {
return b;
}
public void setB(B b) {
this.b = b;
}
@Override
public String toString() {
return "A [age=" + age + ", name=" + name + ", price=" + price + ", b=" + b + "]";
}
}
类A的成员变量 ,类B
public class B implements Serializable{
private String ClassName = "计算机科学与技术";
private int ClassNo = 111;
public String getClassName() {
return ClassName;
}
public void setClassName(String className) {
ClassName = className;
}
public int getClassNo() {
return ClassNo;
}
public void setClassNo(int classNo) {
ClassNo = classNo;
}
@Override
public String toString() {
return "B [ClassName=" + ClassName + ", ClassNo=" + ClassNo + "]";
}
}
测试方法
public class CloneLearn {
public static Object cloneByCode(A a) throws IOException, ClassNotFoundException, CloneNotSupportedException {
return a.clone();
}
public static void main(String[] args) throws ClassNotFoundException, IOException, CloneNotSupportedException {
A a = new A();
a.setAge(123);
A a2 = (A) a.clone();
System.out.println(a);
System.out.println(a2);
System.out.println(a == a2);
System.out.println(a.getB() == a2.getB());
a.getB().setClassName("物联网");
a.setAge(222);
System.out.println(a);
System.out.println(a2);
}
}
运行结果
在这里我们可以看出来重写了Object的clone方法以后,实现的是深拷贝
因为当我们将a的age属性修改成222后,a2的age属性还是123
但是还有一个问题,那就是我们的对象类型的成员变量,还是浅拷贝
因为,当我们修改a的b属性的className后,a2也随之改变
因为我们本来在a这个对象里存的就是引用,所以你再怎么深拷贝,复制的还是引用,而不是真正的数据
现在我们想像b这样特殊的成员变量也进行深拷贝,这个时候就需要将b也重写Object的clone方法
在b类中重写clone方法
@Override
protected Object clone() throws CloneNotSupportedException {
B cloneB = (B) super.clone();
return cloneB;
}
修改a类的clone方法
@Override
protected Object clone() throws CloneNotSupportedException {
A cloneA = (A) super.clone();
cloneA.b = (B)b.clone();
return cloneA;
}
运行结果
这样我们就完全的实现了对象的深拷贝方法,
但是这样做有个缺点,那就是必须的重写clone方法,单写一个类还好,当我们碰到一个类中有多个对象类型的成员变量时,我们就得把那些类全部都重写一遍,是不是瞬间爆炸啊
通过序列化操作
序列化工具类
public class SerializeLearn<E> implements Serializable{
//序列化读写的文件
private static final File file = new File("D:\\serializeLearn\\123.txt");
/**
* 序列化本对象
* @throws IOException
*/
public void Serialize(E obj) throws IOException{
System.out.println(obj);
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file));
oos.writeObject(obj);
}
/**
* 反序列化本对象
* @throws IOException
* @throws FileNotFoundException
* @throws ClassNotFoundException
*/
public E DeSerialize() throws FileNotFoundException, IOException, ClassNotFoundException{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
return (E) ois.readObject();
}
}
让 类A与类B都继承Serializable接口
测试方法
public class CloneLearn {
public static void main(String[] args) throws ClassNotFoundException, IOException, CloneNotSupportedException {
A a = new A();
a.setAge(123);
SerializeLearn<A> util = new SerializeLearn();
util.Serialize(a);
A a2 = util.DeSerialize();
System.out.println(a);
System.out.println(a2);
System.out.println(a == a2);
System.out.println(a.getB() == a2.getB());
a.getB().setClassName("物联网");
a.setAge(222);
System.out.println(a);
System.out.println(a2);
}
}
运行结果
通过序列化的方式,我们用几行代码就实现了深拷贝,
但是也有个重大的缺陷,就是序列化需要进行磁盘操作,那效率真的让人头大