Java中的对象拷贝


对象拷贝是一种创建对象精确副本的方法。对象类的clone()方法就是用于拷贝对象的。

为什么需要拷贝对象?直接new一个对象不行吗?

答:拷贝的对象可能包含一些已经修改过的属性,而new出来的对象的属性都还是初始化时候的值,所以当需要一个新的对象来保存当前对象的“状态”就靠clone方法了。如果把这个对象的临时属性一个一个的赋值给我新new的对象的话,可以是可以,但是一是麻烦,二是clone是一个native方法,在底层实现的,速度快。

tips:

们常见的Object a=new Object();Object b;b=a;这种形式的代码复制的是引用,即对象在内存中的地址,a和b对象仍然指向了同一个对象。而通过clone方法赋值的对象跟原来的对象时同时独立存在的。

拷贝的分类

拷贝有两种不同的方法

(1) 浅拷贝(ShallowClone)

即分配一个新的、未初始化的对象,并复制其中原始对象的所有字段(属性)。当字段值是对对象的引用时,这并不总是导致所需的行为,因为它复制了引用,因此引用了与原始对象相同的对象。因此,引用的对象是共享的,因此,如果其中一个对象被修改,那么更改在另一个对象中是可见的。

(2)深拷贝(DeepClone)

在深拷贝中,无论原型对象的成员变量是值类型还是引用类型,都将复制一份给拷贝对象,深拷贝将原型对象的所有引用对象也复制一份给拷贝对象。

简单来说,在深拷贝中,除了对象本身被复制外,对象所包含的所有成员变量也将复制

如何实现拷贝
浅拷贝

在Java语言中,通过覆盖Object类的clone()方法可以实现浅拷贝

1)不包含引用属性(String类型比较特别,不计入内)的对象浅拷贝

public class copyTest {
    public static void main(String[] args) throws CloneNotSupportedException {
        Student s1 = new Student(1001,"lucy");
        Student s2 = (Student) s1.clone();
        System.out.println(s1);
        System.out.println(s2);
    }
}
class Student implements Cloneable{
    private int id;
    private String name;
    public Student(int id, String name) {
        this.id = id;
        this.name = name;
    }
    @Override
    public Object clone(){
        Student student = null;
        try {
            student = (Student)super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return student;
    }
}

output:

com.dgut.Student@16b98e56
com.dgut.Student@7ef20235

2)包含引用属性的对象浅拷贝

class Student implements Cloneable{
    private int id;
    private String name;
    private Book book;

    public Student(int id, String name, Book book) {
        this.id = id;
        this.name = name;
        this.book = book;
    }

    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 Book getBook() {
        return book;
    }

    public void setBook(Book book) {
        this.book = book;
    }

    @Override
    public Object clone(){
        Student student = null;
        try {
            student = (Student)super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return student;
    }
}

class Book implements Cloneable {
    private String bookName;

    public String getBookName() {
        return bookName;
    }

    public void setBookName(String bookName) {
        this.bookName = bookName;
    }

    public Book(String bookName) {
        this.bookName = bookName;
    }

    @Override
    public String toString() {
        return "Book{" +
                "bookName='" + bookName + '\'' +
                '}';
    }
}

public class copyTest {
    public static void main(String[] args) throws CloneNotSupportedException {
        Book book = new Book("Thinking in Java");
        Student s1 = new Student(1001,"lucy",book);
        Student s2 = (Student) s1.clone();
        //只改变s1的引用变量的内容
        s1.getBook().setBookName("I live in Haven");
        System.out.println(s1);
        System.out.println(s2);
        System.out.println("s1:" + s1.getBook());
        System.out.println("s2:" + s2.getBook());
    }
}

output:

com.dgut.Student@16b98e56
com.dgut.Student@7ef20235
s1:Book{bookName='I live in Haven'}
s2:Book{bookName='I live in Haven'}

根据结果可知,两个Student对象的Book属性内容都变了,因为浅拷贝只是复制了Book变量的引用,并没有真正的开辟另一块空间,将值复制后再将引用返回给新对象,要避免此结果可以考虑深拷贝。

深拷贝

在Java语言中,如果需要实现深拷贝,可以通过覆盖Object类的clone()方法实现,也可以通过序列化(Serialization)等方式来实现。

1)使用clone()方法,我们可以对其内的引用类型的变量,再进行一次 clone(),以此达到深拷贝效果。

public class DeepCopyTest {
    public static void main(String[] args) {
        Addr addr = new Addr("庞各庄");
        Person per1 = new Person(1002,"jack",addr);
        Person per2 = (Person) per1.clone();
        addr.setDesc("大兴岭");
        System.out.println(per1.getAddr());
        System.out.println(per2.getAddr());
    }
}
class Person implements Cloneable {
    private int id;
    private String name;
    private Addr addr;

    public Person(int id, String name, Addr addr) {
        this.id = id;
        this.name = name;
        this.addr = addr;
    }
    public Addr getAddr() {
        return addr;
    }
    @Override
    protected Object clone() {
        Person person = null;
        try {
            person = (Person)super.clone();   //浅拷贝
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        person.addr = (Addr)addr.clone();    //深拷贝
        return person;
    }

}
class Addr implements Cloneable {
    private String desc;

    public Addr(String desc) {
        this.desc = desc;
    }
    public void setDesc(String desc) {
        this.desc = desc;
    }
    @Override
    public String toString() {
        return "Addr{" +
                "desc='" + desc + '\'' +
                '}';
    }
    @Override
    protected Object clone() {
        Addr addr = null;
        try {
            addr = (Addr)super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return addr;
    }
}

2)但如果引用类型里面还包含很多引用类型,或者内层引用类型的类里面又包含引用类型,使用clone方法就会很麻烦。这时我们可以用序列化的方式来实现对象的深拷贝。

class Computer implements Serializable {
    private String brand;
    private Keyboard keyboard;

    public Keyboard getKeyboard() {
        return keyboard;
    }

    public Computer(String brand, Keyboard keyboard) {
        this.brand = brand;
        this.keyboard = keyboard;
    }

    public Computer myclone() {
        Computer computer = null;
        try{
            //将该对象序列化成流
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(this);

            //将流序列化成对象
            ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(baos.toByteArray());
            ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
            computer = (Computer) objectInputStream.readObject();
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
        return computer;
    }
}
class Keyboard implements Serializable {
    private String color;
    public Keyboard(String color) {
        this.color = color;
    }
}
public class DeepCopyTest2 {
    public static void main(String[] args) {
        Keyboard keyboard = new Keyboard("black");
        Computer c1 = new Computer("dell",keyboard);
        Computer c2 = c1.myclone();
        System.out.println(c1.getKeyboard() == c2.getKeyboard());
    }
}

output:

false

你可能感兴趣的:(Java,java,object)