How to clone an object correctly?

There are three ways to clone an object in Java:
  1. Overriding the clone () method and implementing the Cloneable () interface;
  2. Using the copy constructor;
  3. Use the serialization mechanism for cloning

Now in order. The first method implies that you will use the mechanism of the so-called “surface cloning” and take care of cloning the field objects yourself. The method clone()in the parent class Object is protected, so you need to override it with the declaration as public. It returns an instance of an object with copied primitive fields and links. And it turns out that the original and its clone link fields point to the same objects. The example below shows how the field of the original object and the clone changes simultaneously.

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

From the example above, it can be seen that in the clone and the original the state of one of the fields changes simultaneously. The next way is to use the copy constructor:

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

The class describes a constructor that takes an object of the same class and initializes the fields of the new object with the values ​​of its fields. The implementation of the field initialization should be completely taken care of by the class developer.

But both of the above methods are full of potential errors and essentially create a copy of the object. The most convenient and flexible way to clone is the serialization mechanism. It consists in storing the object in a stream of bytes with subsequent exhumation of it from there. For example, we invite the cat Vaska, he is waiting for a couple of experiments:

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}

Not a single cat was injured as a result of the tests, we see that Vaska was saved to the stream from which the independent clone was then restored. If there is no special need to process fields during cloning of objects, then serialization is the most preferable option for these purposes.

Also popular now: