- Клонирование
- 2. Переопределение метода clone()
- Пример 1. Поверхностное клонирование
- Пример 2. Глубокое клонирование
- 3. Конструктор копирования
- Пример 3. Конструктор копирования с поверхностным клонированием
- Пример 4. Конструктор копирования с «глубоким» клонированием
- Копировать объекты в Java
- 1. Использование конструктора копирования или фабрики
- Метод clone, интерфейс Cloneable
- Метод Clone() в Java
- 1. Использование Object.clone() метод
Клонирование
Иногда необходимо на основе существующего объекта создать второй такой же — то есть создать его клон. Это процесс в Java называется клонированием.
Для клонирования объекта в Java можно воспользоваться тремя способами:
- Переопределение метода clone() и реализация интерфейса Cloneable().
- Использование конструктора копирования.
- Использовать для клонирования механизм сериализации.
2. Переопределение метода clone()
Класс Object определяет метод clone(), который создает копию объекта. Если вы хотите, чтобы экземпляр вашего класса можно было клонировать, необходимо переопределить этот метод и реализовать интерфейс Cloneable. Интерфейс Clonable — это интерфейс маркер, он не содержит ни методов, ни переменных. Интерфейсы маркер просто определяют поведение классов.
Object.clone() выбрасывает исключение CloneNotSupportedException при попытке клонировать объект не реализующий интерфейс Cloneable.
Метод clone() в родительском классе Object является protected, поэтому желательно переопределить его как public. Реализация по умолчанию метода Object.clone() выполняет неполное/поверхностное (shallow) копирование. Рассмотрим пример:
Пример 1. Поверхностное клонирование
public class Car implements Cloneable < private String name; private Driver driver; public Car(String name, Driver driver) < this.name = name; this.driver = driver; >public String getName() < return name; >public void setName(String name) < this.name = name; >public Driver getDriver() < return driver; >public void setDriver(Driver driver) < this.driver = driver; >@Override public Car clone() throws CloneNotSupportedException < return (Car) super.clone(); >>
public class Driver implements Cloneable < private String name; private int age; public Driver(String name, int age) < this.name = name; this.age = age; >public String getName() < return name; >public void setName(String name) < this.name = name; >public int getAge() < return age; >public void setAge(int age) < this.age = age; >@Override public Driver clone() throws CloneNotSupportedException < return (Driver) super.clone(); >>
public class CloneCarDemo < public static void main(String[] args) throws CloneNotSupportedException < Car car = new Car("Грузовик", new Driver("Василий", 45)); Car clonedCar = car.clone(); System.out.println("Оригинал:\t" + car); System.out.println("Клон: \t" + clonedCar); Driver clonedCarDriver = clonedCar.getDriver(); clonedCarDriver.setName("Вася"); System.out.println("Оригинал после изменения имени водителя:\t" + car); System.out.println("Клон после изменения имени водителя: \t\t" + clonedCar); >>
В этом примере клонируются объект класса Car. Клонирование выполняется поверхностное — новый объект clonedCar содержит ссылку на тот же объект класса Driver, что и объект car. Если вас это не устраивает, то необходимо самим написать «глубокое» клонирование — создать новый объект класса Driver. Перепишем метод clone() класса Car:
Пример 2. Глубокое клонирование
@Override public Car clone() throws CloneNotSupportedException
3. Конструктор копирования
Еще один вариант клонирования объекта — это конструктор копирования. Создается конструктор, принимающий на вход объект того же класса, который необходимо клонировать:
Пример 3. Конструктор копирования с поверхностным клонированием
public class Car implements Cloneable < private String name; private Driver driver; public Car(String name, Driver driver) < this.name = name; this.driver = driver; >/** * Конструктор копирования. * * @param otherCar */ public Car(Car otherCar) < this(otherCar.getName(), otherCar.getDriver()); >public String getName() < return name; >public void setName(String name) < this.name = name; >public Driver getDriver() < return driver; >public void setDriver(Driver driver) < this.driver = driver; >>
Опять же — пример показывает неглубокое клонирование. Перепишем конструктор для реализации «глубокого» копирования:
Пример 4. Конструктор копирования с «глубоким» клонированием
public Car(Car otherCar) throws CloneNotSupportedException
Копировать объекты в Java
В этом посте будут обсуждаться различные методы копирования объектов в Java. Копирование объекта — это создание копии существующего объекта для изменения или перемещения скопированного объекта без воздействия на исходный объект.
В отличие от C++, объекты Java передаются не по значению, а по значению передаются их ссылки. Рассмотрим следующие фрагменты кода,
Object ob1 = new Object();
Object ob2 = ob1;
Оба ob1 а также ob2 теперь указывает на тот же объект, так как это просто ссылка на исходные данные; здесь не происходит копирования.
Object ob = new Object();
fun(ob);
Это также просто ссылка на исходные данные, поскольку методы в Java всегда передаются по значению. Однако передается значение ссылочной переменной.
Так как данные здесь примитивные, то просто копируется значение примитивного типа.
Иногда необходимо создать копию объекта для использования в приложении. В этом посте мы обсудим несколько способов достижения этого. Прежде чем мы начнем, давайте поговорим о мелком и глубоком копировании объектов.
1.Поверхностное копирование (копирование поля за полем)
Одним из способов копирования объекта является мелкая копия в котором мы выделяем новый неинициализированный объект и копируем в него все поля (атрибуты) из исходного объекта. Это не всегда приводит к желаемому поведению, когда значение поля является ссылкой на объект, поскольку оно копирует ссылку и, следовательно, ссылается на тот же объект, что и исходный объект. Таким образом, ссылочные объекты являются общими, поэтому, если один объект изменен, изменение видно в другом.
Альтернативой мелкому копированию является глубокая копия, где новые объекты создаются для любых объектов, на которые есть ссылки, а не для ссылок на копируемые объекты. Глубокая копия предпочтительнее мелкой копии.
Существует несколько подходов к копированию объекта, как описано ниже:
1. Использование конструктора копирования или фабрики
С помощью Копировать конструктор, мы можем определить действия, выполняемые компилятором при копировании объекта класса. Любая реализация конструктора копирования должна выполнять глубокое копирование для любых объектов, на которые есть ссылки в классе, путем создания новых объектов и копирования значений для примитивных и неизменяемых типов. Обычно он принимает только один параметр, который является еще одним экземпляром того же класса.
Мы также можем использовать метод статической фабрики копий, чтобы по существу делать то же самое, что и метод конструктора копирования. Оба подхода показаны ниже:
Метод clone, интерфейс Cloneable
Цель этого метода – клонировать объект – т.е. создать его клон/копию/дубликат.
Если его вызвать, то Java-машина создаст и вернет дубликат объекта, у которого вызвали этот метод.
Клонирование объекта в классе Object реализовано очень примитивно – при клонировании создается всего один новый объект: просто создается еще один объект и его полям присваиваются значения полей объекта-образца.
Если копируемый объект содержит ссылки на другие объекты, то ссылки будут скопированы, дубликаты тех объектов не создаются.
— Дело в том, что Java-машина не знает, какие объекты можно клонировать, а какие нет. Файлы, например, клонировать нельзя. Как и поток System.in.
Поэтому вопрос о полноценном клонировании был отдан на откуп разработчикам классов. Тут все было сделано по аналогии с методом equals. Даже есть свой аналог hashCode – это интерфейс Cloneable.
Интерфейс Cloneable – это так называемый интерфейс-маркер, который не содержит никаких методов. Он используется, чтобы маркировать (помечать) некоторые классы.
Если разработчик класса считает, что объекты класса можно клонировать, он помечает класс этим интерфейсом (наследует класс от Cloneable).
Если разработчика не устраивает стандартная реализация метода clone, он должен написать свою, которая будет создавать дубликат объекта правильным образом.
При вызове метода clone(), Java проверяет, был ли у объекта интерфейс Cloneable. Если да — клонирует объект методом clone(), если нет — выкидывает исключение CloneNotSupportedException.
— Т.е. мы должны или переопределить метод clone и написать его новую реализацию или унаследовать класс от Cloneable?
— Да, но переопределять метод все же придется. Метод clone() объявлен как protected, так что он доступен для вызова только классам из его пакета (java.lang.*) или классам-наследникам.
— Я немного запутался, так что же надо сделать, чтобы клонировать объект?
— Смотри, если ты хочешь воспользоваться «клонированием по умолчанию», которое реализовано в классе Object, тебе нужно:
а) Добавить интерфейс Cloneable своему классу
б) Переопределить метод clone и вызвать в нем базовую реализацию:
class Point implements Cloneable >
Или ты можешь написать реализацию метода clone полностью сам:
— Интересный метод, буду пользоваться. Иногда…
Метод Clone() в Java
В этом посте будет обсуждаться, как скопировать объект в Java с помощью clone() метод в Object class и один, предоставленный Apache Commons Lang. Мы также подробно обсудим мелкое и глубокое копирование.
Клонирование объекта создает копию существующего объекта для изменения или перемещения скопированного объекта, не затрагивая исходный объект. В Java управление объектами осуществляется с помощью ссылочных переменных, и нет оператора для фактического копирования объекта. Помните, что оператор присваивания дублирует ссылку, а не объект.
1. Использование Object.clone() метод
Классы, которым нужна функция копирования, могут использовать Object.clone() метод, который создает и возвращает копию объекта. Прототип Object.clone() является
В качестве возвращаемого типа Object.clone() является Object , приведение типов необходимо для присвоения возвращаемой ссылки Object ссылке на объект.
Все задействованные классы должны реализовывать Cloneable интерфейс для указания Object.clone() метод о том, что для этого метода разрешено создавать полевые копии экземпляров этого класса. Вызов метода clone объекта для экземпляра, который не реализует Cloneable интерфейс приводит к CloneNotSupportedException .
Поскольку каждый класс неявно расширяет Object учебный класс, Object.clone() является переопределяемым методом. Поскольку Java поддерживает ковариантные возвращаемые типы, возвращаемый тип clone() можно изменить с Object на тип клонируемого объекта, и clone() должен переопределить защищенный Object.clone() метод с общедоступным методом.
The clone() ведет себя так же, как Копировать конструктор. Он называет clone() метод своего родительского класса для получения копии и т. д., пока он в конечном итоге не достигнет класса Object. clone() метод, который создает новый экземпляр того же класса, поскольку объект копирует все поля в новый экземпляр.
1. Object.clone() не будет работать с интерфейсами и абстрактными классами.
Единственный способ использовать Object.clone() метод, если класс объекта известен, т. е. мы не можем получить доступ к clone() метод для абстрактного типа, так как большинство интерфейсов и абстрактных классов в Java не определяют общедоступный clone() метод.
Например, нельзя вызвать clone() в ссылке на карту в Java, потому что карта не указывает общедоступного clone() метод. Только реализации карты, такие как HashMap а также LinkedHashMap имеют clone() методы, но носить с собой тип класса объекта не рекомендуется, и это противоречит принципу “программа для интерфейса, а не для реализации”.
2. Реализация по умолчанию Object.clone() метод возвращает Неглубокое копирование.
При неглубоком копировании, если значение поля является примитивным типом, оно копирует свое значение; в противном случае, если значение поля является ссылкой на объект, оно копирует ссылку и, следовательно, ссылается на тот же объект. Теперь, если один из этих объектов изменен, изменение будет видно в другом. В Глубокое копирование, в отличие от мелкой копии, объекты, на которые есть ссылки, не являются общими; вместо этого новые объекты создаются для любых объектов, на которые есть ссылки.
Следующая программа демонстрирует использование Object.clone() метод, используя его реализацию по умолчанию, которая возвращает поверхностную копию. Мы рассмотрим глубокое копирование с помощью clone() метод в следующем разделе.