Для клонирования объекта в Java можно пользоваться тремя способами: ​

  1. Переопределение метода clone() и реализация интерфейса Cloneable().
  2. Использование конструктора копирования.
  3. Использовать для клонирования механизм сериализации

А теперь по порядку. Первый способ подразумевает, что Вы будете использовать механизм так называемого «поверхностного клонирования»+сами позаботитесь о клонировании полей-объектов. Метод clone() в родительском классе Object является protected, поэтому требуется переопределение его с объявлением как public. Он возвращает экземпляр объекта с копированными полями-примитивами и ссылками. И получается что у оригинала и его клона поля-ссылки указывают на одни и те же объекты. Пример далее показывает как меняется одновременно поле у оригинального объекта и клона.

public class CloneTest{
    static class Person implements Cloneable{
        String name;
        int age;
        Car car;
        Person(Car car,int age,String name) {
            this.car = car;
            this.age = age;
            this.name = name;
        }

        @Override
        public String toString() {
            return this.name+" {" +
                    "age=" + age +
                    ", car=" + car +
                    '}';
        }

        @Override
        protected Object clone() throws CloneNotSupportedException {
            return super.clone();
        }
    }
    static class Car{
        public String color;

        Car(String color) {
            this.color = color;
        }

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

    public static void main(String[] args) throws CloneNotSupportedException {
        Car car = new Car("Green");
        Person person=new Person(car,25,"Mike");

        Person clone = (Person) person.clone();
        System.out.println(person);
        System.out.println(clone);
        clone.name=new String("Ivan");
        clone.car.color="red";
        System.out.println(person);
        System.out.println(clone);
    }
}
    Вывод:
        Mike {age=25, car={color car='Green'}}
    Mike {age=25, car={color car='Green'}}
    Mike {age=25, car={color car='red'}}
    Ivan {age=25, car={color car='red'}}

Из примера выше видно, что у клона и оригинала состояние одного из полей изменяется одновременно. Следующий способ заключается в использовании конструктора копирования:

public class Person {
        private int age;
        private String name;
        public Person(int age, String name){
            this.age=age;
            this.name=name;
        }
        // конструктор копии
        public Person(Person other) {
            this(other.getAge(), other.getName());
        }

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

        @Override
        public String toString() {
            return "Person{" +
                    "age=" + age +
                    ", name='" + name + '\'' +
                    '}';
        }

        public static void main(String[] args) {
            Person original = new Person(18, "Grishka");
            Person clone = new Person(original);
            System.out.println(original);
            System.out.println(clone);
        }
}
    Вывод:
    Person{age=18, name='Grishka'}
    Person{age=18, name='Grishka'}

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

Но оба вышеуказанных способа полны потенциальных ошибок и по сути создают копию объекта. Наиболее удобным и гибким способом клонирования является механизм сериализации. Он заключается в сохранении объекта в поток байтов с последующей эксгумацией его от туда. Для примера пригласим кота Ваську, его ждёт пара опытов:

import java.io.*;

class Cat implements Serializable{
    private String name;
    private String color;
    private int age;

    public Cat(String name, String color, int age) {
        this.name = name;
        this.color = color;
        this.age = age;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    @Override
    public String toString() {
        return "Cat{" +
                "name='" + name + '\'' +
                ", color='" + color + '\'' +
                ", age=" + age +
                '}';
    }
}
public class BasketCats{
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        Cat vaska = new Cat("Vaska","Gray",4);
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream ous = new ObjectOutputStream(baos);
        //сохраняем состояние кота Васьки в поток и закрываем его(поток)
        ous.writeObject(vaska);
        ous.close();
        ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bais);
        //создаём кота для опытов и инициализируем его состояние Васькиным
        Cat cloneVaska = (Cat)ois.readObject();
        System.out.println(vaska);
        System.out.println(cloneVaska);
        System.out.println("*********************************************");
        cloneVaska.setColor("Black");
        //Убеждаемся что у кота Васьки теперь есть клон, над которым можно ставить опыты без ущерба Василию
        System.out.println(vaska);
        System.out.println(cloneVaska);

    }
}
    Вывод:
        Cat{name='Vaska', color='Gray', age=4}
    Cat{name='Vaska', color='Gray', age=4}
    *********************************************
    Cat{name='Vaska', color='Gray', age=4}
    Cat{name='Vaska', color='Black', age=4}

Ни один кот не пострадал в результате тестов, мы видим что Васька был сохранён в поток, из которого затем восстановили независимый клон. Если нет особой необходимости обработки полей во время клонирования объектов, то сериализация является наиболее предпочтительным вариантом для этих целей. Так же можно указать transient поля,которые не обязательно восстанавливать, но об этом в следующих вопросах…

Понравилась статья? Поделиться с друзьями:

Комментарии:

Добавить комментарий

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!: