赞
踩
Java 赋值是复制对象引用
,实际上两个引用指向的是同一个对象。如果我们想要得到一个对象的副本,使用赋值操作是无法达到目的的,这里就需要用到 clone;
public class Main {
public static void main(String[] args) {
Student stu1 = new Student("小黑", 20);
Student stu2 = stu1;
System.out.println(stu1 == stu2); // true
}
}
如果创建一个对象的新的副本,也就是说他们的初始状态完全一样,但以后可以改变各自的状态,而互不影响,就需要用到 Java 中对象的复制,如原生的 clone() 方法。
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
浅拷贝:被复制对象的所有值属性都含有与原来对象的相同,而所有的对象引用属性仍然指向原来的对象。
深拷贝:在浅拷贝的基础上,所有引用其他对象的变量也进行了clone,并指向被复制过的新对象。
Object对象有个clone()方法,实现了对象中各个属性的复制,但它的可见范围是protected的,所以实体类使用克隆的前提是:
① 实现Cloneable接口,这是一个标记接口,自身没有方法。
② 覆盖clone()方法,可见性提升为public。
public class Main { public static void main(String[] args) throws CloneNotSupportedException { Student stu1 = new Student("小黑", 20); Student stu2 = stu1.clone(); System.out.println(stu1 == stu2); // false } } class Student implements Cloneable { // 实现接口 String name; int age; Student(String name, int age) { this.name = name; this.age = age; } @Override public Student clone() throws CloneNotSupportedException { return (Student) super.clone(); } }
副作用:
当对象的成员属性不仅仅有基本数据类型、还有对象类型时,浅拷贝后的两个对象的成员对象都指向原来的成员对象,也就是说内部的成员对象并没有拷贝,而是复制引用。
public class Main {
public static void main(String[] args) throws CloneNotSupportedException {
Student stu1 = new Student("小黑", 20);
Student stu2 = stu1.clone();
System.out.println(stu1 == stu2); // false, 不是同一个对象
System.out.println(stu1.address.addr); // stu1的成员对象address的值为 西安工程大学
stu2.address.addr = "西安科技大学"; // 修改了stu2的成员对象address的值
System.out.println(stu1.address.addr); // stu1的成员对象address的值也改变了
System.out.println(stu2.address.addr); // 说明浅拷贝并没有拷贝对象内部的对象类型
}
}
如果被复制对象的属性包含其他实体类对象引用,那么这些实体类对象都需要实现cloneable接口并覆盖clone()方法。
① Student类 和 它的成员实体类 都实现 Cloneable 接口。
② 成员实体类 简单地覆盖 clone() 方法。
③ Student 的 clone() 需要增加成员的克隆逻辑。
public class Main { public static void main(String[] args) throws CloneNotSupportedException { Student stu1 = new Student("小黑", 20); Student stu2 = stu1.clone(); System.out.println(stu1 == stu2); // false System.out.println(stu1.address.addr); // stu1.address = "西安工程大学" stu2.address.addr = "西安科技大学"; // 修改 stu2.address System.out.println(stu1.address.addr); // stu1.address = "西安工程大学" System.out.println(stu2.address.addr); // stu2.address = "西安科技大学" } } class Student implements Cloneable { String name; int age; Address address; Student(String name, int age) { this.name = name; this.age = age; this.address = new Address(); } // Student 的clone()需要显式地clone其引用成员。 @Override public Student clone() throws CloneNotSupportedException { Student newStu = (Student) super.clone(); Address newAdd = newStu.address.clone(); newStu.address = newAdd; return newStu; } } class Address implements Cloneable { String addr = "西安工程大学"; @Override public Address clone() throws CloneNotSupportedException { return (Address) super.clone(); } }
虽然层次调用clone方法可以实现深拷贝,但是显然代码量实在太大。特别对于属性数量比较多、层次比较深的类而言,每个类都要重写clone方法太过繁琐。
将对象序列化为字节序列后,默认会将该对象的整个对象图进行序列化,再通过反序列即可完美地实现深拷贝。
public class ObSriaInOut { public static void main(String[] args) throws IOException, ClassNotFoundException{ Person p=in(); Person p2=out(); System.out.println(p); System.out.println(p2); } public static Person out() throws IOException, FileNotFoundException { //創建對象輸出流,在使用對象輸出流時候必須進行對象序列化,否知會報錯java.io.NotSerializableException ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("f:\\person.bin",false)); //創建一個persn對象 Person p = new Person("張大",22,5000); //寫出對象流 oos.writeObject(p); //關閉流 oos.flush(); oos.close(); return p; } public static Person in() throws IOException, FileNotFoundException, ClassNotFoundException { //創建對象輸入流 ObjectInputStream ois = new ObjectInputStream(new FileInputStream("f:\\person.bin")); //讀取對象流 Person p = (Person) ois.readObject(); ois.close(); System.out.println(p.toString()); return p; } } class Person implements Serializable{ private String name; private double sal; private int age; public Person() { super(); } //構造方法用於初始化成員參數 public Person(String name, double sal, int age) { super(); this.name = name; this.sal = sal; this.age = age; } @Override public String toString() { return this.name+"---"+this.age+"---"+this.sal; } }
总结:
提示:这里对文章进行总结:
以上就是今天的学习内容,本文是JavaSE的学习,认识什么是拷贝,浅拷贝和深拷贝的区别,实现深拷贝的两种方式。之后的学习内容将持续更新!!!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。