(Базовая серия) Использование, принцип и цель клонирования объекта

Java

предисловие

Информации по объектному клону (object clone) в интернете очень много, так зачем мне писать эту статью? Основная цель состоит в том, чтобы собрать отличные моменты из нескольких статей и мое понимание клонирования объектов, чтобы углубить впечатление, а также дать возможность читателям более полно понять использование, принцип и цель клонирования объектов.

1. Что такое «клон объекта»

Как следует из названия, клон — это копия того же самого, реплика конкретного существования, термин, который стал привычным с самого начала биологической науки. В компьютерной индустрии этот термин широко используется для обозначения имитации IBM PC компаниями Compaq, Dell и другими. В языке java метод clone вызывается объектом, поэтому объект копируется.

Во-вторых, использование клонирования объекта

(1) Краткое описание метода

сфера тип метод описывать
protected Object clone() Клонировать объект, реализующий интерфейс Cloneable

Примечание: метод clone модифицируется нативным.Проще говоря, модифицированный нативным методом метод указывает на конкретную реализацию кода, отличного от Java, при его вызове.Эта реализация может быть на других языках или операционных системах.

(2) правила клонирования:

1、 基本类型  
    如果变量是基本类型,则拷贝其值,比如int、float等。
2、 对象  
    如果变量是一个实例对象,则拷贝其地址引用,也就是说新对象和原来对象是共用实例变量的。
3、 String字符串  
    若变量为String字符串,则拷贝其地址引用。但是在修改时,它会从字符串池中重新生成一个新的字符串,原有的对象保持不变。

(2) Пример 1:

实现clone方法的步骤:
1. 实现Cloneable接口 
2. 重载Object类中的clone()方法,重载时需定义为public 
3. 在重载方法中,调用super.clone()
public class Book implements Cloneable {

    private int id;

    private String name;

    public Book() {}

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

    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;
    }

    @Override
    public Object clone() throws CloneNotSupportedException {
        return (Book)super.clone();
    }

    public static void main(String[] args) throws CloneNotSupportedException {
        Book book1 = new Book();
        book1.setName("基础系列1");
        Book book2 = (Book) book1.clone();

        System.out.println("图书1:" + book1.getName());
        System.out.println("图书2:" + book2.getName());

        book2.setName("基础系列2");

        System.out.println("图书1:" + book1.getName());
        System.out.println("图书2:" + book2.getName());

    }
}

результат операции:

图书1:基础系列1
图书2:基础系列1
图书1:基础系列1
图书2:基础系列2

Судя по текущим результатам, это должен быть глубокий клон, но почему он поверхностный? отстрока неизменна(Атрибут строки в исходном объекте и клонированный объект относятся к одному и тому же адресу) Результат должен быть поверхностным клоном, но это глубокий клон результата, поэтому с этой точки зрения клон является глубоким клоном строки. .

Примечание. CloneNotSupportedException будет выброшено, если класс без реализации Cloneable вызывает метод Object.clone().

(3) Пример 2:

public class Book implements Cloneable {

    //在示例1的基础上增加bookBorrow的引用
    private BookBorrow bookBorrow;

    public Book() {}

    public Book(int id, String name, BookBorrow bookBorrow) {
        this.id = id;
        this.name = name;
        this.bookBorrow = bookBorrow;
    }

    public BookBorrow getBookBorrow() {
        return bookBorrow;
    }

    public void setBookBorrow(BookBorrow bookBorrow) {
       this.bookBorrow = bookBorrow;
    }

    @Override
    public Object clone() throws CloneNotSupportedException {
        Book book = (Book)super.clone();
        //这里注释掉就是浅克隆,否则就是深克隆
        book.bookBorrow = (BookBorrow)bookBorrow.clone();
        return book;
    }

    @Override
    public String toString() {
        return "BOOK[id="+id+",name="+name+",bookBorrow:"+bookBorrow+"]";
    }

    public static void main(String[] args) throws CloneNotSupportedException {

        BookBorrow bookBorrow = new BookBorrow(1,1);
        Book book1 = new Book(1,"基础系列1",bookBorrow);
        Book book2 = (Book) book1.clone();

        System.out.println("图书1:" + book1.toString());
        System.out.println("图书2:" + book2.toString());

        book2.setName("基础系列2");
        book2.setBookBorrow(new BookBorrow(5,5));

        System.out.println("图书1:" + book1.toString());
        System.out.println("图书2:" + book2.toString());

    }
}

public class BookBorrow  implements Cloneable{

    private int id;

    private int borstate;


    public BookBorrow(int id, int borstate) {
        this.id = id;
        this.borstate = borstate;
    }

    public int getId() {
        return id;
    }

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

    public int getBorstate() {
        return borstate;
    }

    public void setBorstate(int borstate) {
        this.borstate = borstate;
    }

    @Override
    public Object clone() throws CloneNotSupportedException {
        return (BookBorrow)super.clone();
    }

    @Override
    public String toString() {
        return "BookBorrow[id="+id+",borstate="+borstate+"]";
    }

}

результат операции:

图书1:BOOK[id=1,name=基础系列1,bookBorrow:BookBorrow[id=1,borstate=1]]
图书2:BOOK[id=1,name=基础系列1,bookBorrow:BookBorrow[id=1,borstate=1]]
图书1:BOOK[id=1,name=基础系列1,bookBorrow:BookBorrow[id=1,borstate=1]]
图书2:BOOK[id=1,name=基础系列2,bookBorrow:BookBorrow[id=5,borstate=5]]

Исходя из результатов, вот стандартная реализация глубокого клонирования.Основная предпосылка реализации глубокого клонирования заключается в том, что реализуется объект, на который ссылается текущий объект, или объект, на который ссылается объект объекта.Общее использование 1И вызовите метод клонирования объекта, на который ссылаются, в перегруженном методе клонирования.

пример:

 @Override
    public Object clone() throws CloneNotSupportedException {
        Book book = (Book)super.clone();
        //这里注释掉就是浅克隆,否则就是深克隆
        book.bookBorrow = (BookBorrow)bookBorrow.clone();
        return book;
    }

Примечание: Пример, приведенный в Примере 2, является относительно простым и распространенным классом.В реальной разработке объект клона может зависеть от стороннего пакета jar или объекта, чей ссылочный уровень слишком глубок для изменения.В этом случае , рекомендуется использовать Практику примера 3, используя клон сериализации.

(3) Пример 3:

Сериализировать класс клона

public class CloneUtils {

    public static <T extends Serializable> T clone(T obj){

        T cloneObj = null;
        try {
            //写入字节流
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            ObjectOutputStream obs = new ObjectOutputStream(out);
            obs.writeObject(obj);
            obs.close();

            //分配内存,写入原始对象,生成新对象
            ByteArrayInputStream ios = new ByteArrayInputStream(out.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(ios);
            //返回生成的新对象
            cloneObj = (T) ois.readObject();
            ois.close();
        } catch (Exception e) {
            e.printStackTrace();
        }

        return cloneObj;
    }
}

public class BookBorrow implements Serializable{
    ...
    //去掉clone方法,继承Serializable

}

public class Book implements Serializable {
    ...
    //去掉clone方法,继承Serializable

    public static void main(String[] args) throws CloneNotSupportedException {

        BookBorrow bookBorrow = new BookBorrow(1,1);
        Book book1 = new Book(1,"基础系列1",bookBorrow);
        Book book2 = CloneUtils.clone(book1);

        System.out.println("图书1:" + book1.toString());
        System.out.println("图书2:" + book2.toString());

        book2.setName("基础系列2");
        book2.setBookBorrow(new BookBorrow(5,5));

        System.out.println("图书1:" + book1.toString());
        System.out.println("图书2:" + book2.toString());

    }
}

Результаты:

图书1:BOOK[id=1,name=基础系列1,bookBorrow:BookBorrow[id=1,borstate=1]]
图书2:BOOK[id=1,name=基础系列1,bookBorrow:BookBorrow[id=1,borstate=1]]
图书1:BOOK[id=1,name=基础系列1,bookBorrow:BookBorrow[id=1,borstate=1]]
图书2:BOOK[id=1,name=基础系列2,bookBorrow:BookBorrow[id=5,borstate=5]]

Клон сериализации не требует наследования, и тот же эффект глубокого клонирования может быть достигнут с помощью класса инструмента сериализации. Однако универсального средства для клонирования не существует.Сериализация менее эффективна, чем оригинальный клон.

Во-вторых, принцип клонирования объекта

Этот учебник объяснит на основе Примера 1:

Тестовый код, размещенный, чтобы не потерять контекст, объяснит принцип клонирования объекта в 2 частях.

public static void main(String[] args) throws CloneNotSupportedException {
    //第一部分
    Book book1 = new Book();
    book1.setName("基础系列1");
    Book book2 = (Book) book1.clone();

    System.out.println("图书1:" + book1.getName());
    System.out.println("图书2:" + book2.getName());

    //第二部分
    book2.setName("基础系列2");

    System.out.println("图书1:" + book1.getName());
    System.out.println("图书2:" + book2.getName());

}

Результат первой части:

图书1:基础系列1
图书2:基础系列1

Схема мелкого клона:

image
image

Из рисунка видно, что имя клона относится к одному и тому же значению, так почему же сказано, что это глубокий клон? Причина в том, что на этом этапе имя не изменяется, поэтому они представляют собой неглубокие клоны, ссылающиеся на одно и то же значение переменной имени. Тогда результат, полученный при выполнении второй части, полностью обособляется от принципиальной схемы, как вы думаете.

Вторая часть результатов выполнения:

图书1:基础系列1
图书2:基础系列2

Схема глубокого клона:

image
image

Из рисунка видно, что если изменить значение атрибута имени, функция clone регенерирует объект из кучи, на который ссылается клонированный объект, в то время как исходный объект остается неизменным. глубокий клон.

Резюме принципа клонирования:

Предыдущее введение принципа основано на примере 1. Принцип примера 2 аналогичен принципу примера 1. Единственное отличие состоит в том, что объектов атрибутов больше, и объекты атрибутов будут копировать только ссылочный адрес в клоне.Для достижения глубокого клонирования , только глубокое клонирование может быть достигнуто путем добавления реализации метода клонирования к объекту, на который ссылаются, или объекту объекта, на который ссылаются.

3. Практическое использование клонирования объекта

1. Тщательно спроектируйте неглубокий объект-клон, который будет кэшироваться программой в качестве шаблона функционального модуля; каждый раз, когда пользователь вызывает этот модуль, переменная часть может быть заменена информацией, необходимой пользователю.
Пример:
Функция: отправить письмо
Описание: Отправляйте электронные письма пользователям в одной группе с одинаковым содержимым электронной почты (неизменяемое) разным пользователям (переменное).

2. Тщательно спроектируйте объект глубокого клонирования. Кэш программы используется в качестве начального объекта функционального модуля, например: «Режим туриста» Каждый турист входит в систему для доступа к начальному объекту, и на основе начального объекта количество разрабатываются различные туристические маршруты. Пока вы можете придумать умный дизайн, многие функции могут быть применены к клонированию объекта.

4. Резюме

Эта статья представляет клонирование объекта в трех частях и знакомит с использованием, принципом и целью клонирования соответственно; клонирование объекта сводится к двум функциям: переменная и неизменяемая.В реальной разработке мы можем спроектировать хорошую производительность на основе этих двух функций. модуль.