浅谈Java中的浅复制和深复制

浅谈Java中的浅克隆和深克隆

1-clone、Cloneable和Serialiable

什么是浅复制?什么是深复制?两者有何区别?要弄清楚这些问题,一切都要从Object的一个方法clone方法和两个标记接口Cloneable和Serializable谈起!

首先来看看clone方法,clone方法是Java中根父类Object的一个方法,定义如下:

protected native Object clone() throws CloneNotSupportedException;

标记接口Cloneable表示实现这个接口的类是可以clone的,Serializable表示实现这个接口的类是可以序列化的,所谓序列化就是这个对象可以通过读写字节码的方式在网络上或各个系统间传输!clone方法的意思就是克隆出一个对象并返回,可以看到Object中clone方法并没有具体的实现,关键字native表示clone方法是个原生方法,方法的具体实现不在这个文件中,而是在其他编程语言实现的文件中,比如C或C++。

2代码说话

定义一个JavaBean,类名Book,表示图书,首先看如果没有实现Cloneable接口时调用clone方法会发生啥!Book的代码如下:

Book类并没有实现Cloneable接口,此时调用clone方法,代码如下:

package jdk.test;

import java.util.List;

/**
 * 图书类
 */
public class Book implements Cloneable {

    private Long id;

    private String bookname;

    private String author;

    private List chapterList;

    public Book() {
    }

    public Book(Long id, String bookname, String author, List chapterList) {
        this.id = id;
        this.bookname = bookname;
        this.author = author;
        this.chapterList = chapterList;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getBookname() {
        return bookname;
    }

    public void setBookname(String bookname) {
        this.bookname = bookname;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    public List getChapterList() {
        return chapterList;
    }

    public void setChapterList(List chapterList) {
        this.chapterList = chapterList;
    }

    /**
     * 重写Object的clone方法
     */
    public Object clone() {
        try {
            return super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return null;
    }
}

Chapter类表示图书的章节,代码如下:

package jdk.test;

/**
 * 书的章节
 */
public class Chapter {

    //章节的名称
    private String name;

    //章节的字数
    private Integer size;

    //章节的页数
    private Integer pageQty;

    public Chapter() {
    }

    public Chapter(String name, Integer size, Integer pageQty) {
        this.name = name;
        this.size = size;
        this.pageQty = pageQty;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getSize() {
        return size;
    }

    public void setSize(Integer size) {
        this.size = size;
    }

    public Integer getPageQty() {
        return pageQty;
    }

    public void setPageQty(Integer pageQty) {
        this.pageQty = pageQty;
    }
}

测试代码如下:

@Test
public void testBook() {
    Chapter chapter1 = new Chapter("第一章", 2500, 15);
    Chapter chapter2 = new Chapter("第二章", 2600, 16);
    Book book = new Book(1l, "三国演义", "罗贯中", Lists.newArrayList(chapter1, chapter2));
    Book cloneBook = (Book) book.clone();
    System.out.println(book == cloneBook);  //false
    System.out.println(book.getChapterList() == cloneBook.getChapterList()); //true

    //给book对象的chapterList加一个元素,可以看到cloneBook的chapter也变化了
    book.getChapterList().add(new Chapter("第三章", 2500, 15));
    System.out.println(cloneBook.getChapterList().size()); //3
}

如果没有实现Cloneable接口,那么运行测试代码,抛出如下异常:

java.lang.CloneNotSupportedException: jdk.test.Book

Book类实现Cloneable接口后再次运行测试代码,正常,打印结果为false/true/3,说明book和cloneBook是两个对象,但是book和cloneBook的chapterList都指向同一个引用,也就是说在调用Book的clone方法进行克隆时,对象的属性值的克隆只是克隆了引用,cloneBook的chapterList中元素的个数也是3,说明对book的属性值修改后,cloneBook的属性值也跟着变化了,这就是浅复制!

浅复制:通过clone方法复制时,对象的属性值的复制通过复制引用的方式完成的对象的复制就叫浅复制!

在来看看深复制,深复制就是复制一个完全不同的对象出来,没有任何关联,只是它们的属性值都是相同的,后续对任何一个的修改或其他操作都不会对另外一个有影响!本文是通过对象流的读写来完成深复制的,代码如下:

通过对象流的读写完成深复制,即序列化的方式,那么类必须实现Serializable接口,属性类的定义也要实现Serializable接口!

@Test
public void testDeepClone() throws Exception{
    Chapter chapter1 = new Chapter("第一章", 2500, 15);
    Chapter chapter2 = new Chapter("第二章", 2600, 16);
    Book book = new Book(1l, "三国演义", "罗贯中", Lists.newArrayList(chapter1, chapter2));
    //将对象转换为字节流写入流中
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    ObjectOutputStream oos = new ObjectOutputStream(baos);
    oos.writeObject(book);

    //从流里读出来
    ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
    ObjectInputStream ois = new ObjectInputStream(bais);
    Book cloneBook = (Book) ois.readObject();
    //关闭流
    baos.close();
    oos.close();
    bais.close();
    ois.close();

    /**
     * 此处如果没有实现Serializable接口,就会报错java.io.NotSerializableException
     * Library持有任何对象的类型都要实现Serializable接口,否则会报错
     */
    System.out.println(book == cloneBook);
    System.out.println(book.getChapterList() == cloneBook.getChapterList());
}

运行的结果打印出false/false,说明此时与浅复制不一样,不是引用的复制了,而是完全不同的两个对象了!这就是深复制!

下面通过一个图来具体介绍浅复制与深复制的区别:

浅谈Java中的浅复制和深复制_第1张图片

图1 浅复制示意图

浅谈Java中的浅复制和深复制_第2张图片

图2 深复制示意图

从图中可以看出,浅复制就是引用的复制,深复制是复制出完全独立的一个对象!




你可能感兴趣的:(Java基础知识)