Рубрики
Без рубрики

Метод клонирования объектов Java () – Клонирование в Java

Метод Java Object clone() создает копию экземпляра. Узнайте о клонировании в Java, Неглубоком копировании против глубокого копирования и некоторых лучших методах клонирования.

Автор оригинала: Pankaj Kumar.

Клонирование – это процесс создания копии объекта. Класс объектов Java поставляется с собственным clone() методом, который возвращает копию существующего экземпляра.

Поскольку Object является базовым классом в Java, все объекты по умолчанию поддерживают клонирование.

Клонирование объектов Java

Если вы хотите использовать метод клонирования объектов Java (), вам необходимо реализовать java.lang.Клонируемый маркерный интерфейс. В противном случае он вызовет CloneNotSupportedException во время выполнения.

Кроме того , клонирование объекта является защищенным методом, поэтому вам придется переопределить его.

Давайте рассмотрим клонирование объектов в Java на примере программы.

package com.journaldev.cloning;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

public class Employee implements Cloneable {

	private int id;

	private String name;

	private Map props;

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

	public Map getProps() {
		return props;
	}

	public void setProps(Map p) {
		this.props = p;
	}

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

}

Мы используем метод клонирования объекта (), поэтому мы реализовали Клонируемый интерфейс . Мы вызываем метод клонирования суперкласса (), т. е. метод клонирования объекта ().

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

Давайте создадим тестовую программу, которая будет использовать метод клонирования объекта() для создания копии экземпляра.

package com.journaldev.cloning;

import java.util.HashMap;
import java.util.Map;

public class CloningTest {

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

		Employee emp = new Employee();

		emp.setId(1);
		emp.setName("Pankaj");
		Map props = new HashMap<>();
		props.put("salary", "10000");
		props.put("city", "Bangalore");
		emp.setProps(props);

		Employee clonedEmp = (Employee) emp.clone();

		// Check whether the emp and clonedEmp attributes are same or different
		System.out.println("emp and clonedEmp == test: " + (emp == clonedEmp));
		
		System.out.println("emp and clonedEmp HashMap == test: " + (emp.getProps() == clonedEmp.getProps()));
		
		// Let's see the effect of using default cloning
		
		// change emp props
		emp.getProps().put("title", "CEO");
		emp.getProps().put("city", "New York");
		System.out.println("clonedEmp props:" + clonedEmp.getProps());

		// change emp name
		emp.setName("new");
		System.out.println("clonedEmp name:" + clonedEmp.getName());

	}

}

Выход:

emp and clonedEmp == test: false
emp and clonedEmp HashMap == test: true
clonedEmp props:{city=New York, salary=10000, title=CEO}
clonedEmp name:Pankaj

Исключение CloneNotSupportedException во время выполнения

Если наш класс сотрудников не будет реализовывать Cloneable интерфейс, вышеуказанная программа выдаст CloneNotSupportedException время выполнения исключение .

Exception in thread "main" java.lang.CloneNotSupportedException: com.journaldev.cloning.Employee
	at java.lang.Object.clone(Native Method)
	at com.journaldev.cloning.Employee.clone(Employee.java:41)
	at com.journaldev.cloning.CloningTest.main(CloningTest.java:19)

Понимание Клонирования объектов

Давайте рассмотрим приведенный выше вывод и поймем, что происходит с методом Object clone () .

  1. emp и clonedEmp: false : Это означает, что emp и clonedEmp являются двумя разными объектами, не относящимися к одному и тому же объекту. Это согласуется с требованием клонирования объектов java.
  2. хэш-карта emp и clonedEmp: true : Таким образом, объектные переменные emp и clonedEmp ссылаются на один и тот же объект. Это может стать серьезной проблемой целостности данных, если мы изменим значение базового объекта. Любое изменение значения может также отразиться на клонированном экземпляре.
  3. clonedEmp,,} : Мы не вносили никаких изменений в свойства clonedEmp, но, тем не менее, они были изменены, потому что переменные emp и clonedEmp ссылаются на один и тот же объект.Это происходит потому, что метод клонирования объекта по умолчанию() создает неглубокую копию. Это может быть проблемой, когда вы хотите создать полностью отделенные объекты с помощью процесса клонирования. Это может привести к нежелательным результатам, следовательно, необходимо правильно переопределить метод клонирования объекта ().
  4. Имя клона:Панкай : Что здесь произошло?Мы изменили имя emp, но имя clonedEmp не изменилось. Это потому, что Строка неизменна . Поэтому, когда мы устанавливаем empname, создается новая строка и ссылка на empname изменяется в this.name; .Следовательно, название clonedemo остается неизменным. Вы также найдете аналогичное поведение для любых типов примитивных переменных. Таким образом, мы хорошо справляемся с клонированием объектов java по умолчанию, пока в объекте есть только примитивные и неизменяемые переменные.

Типы клонирования объектов

Существует два типа клонирования объектов – поверхностное клонирование и глубокое клонирование. Давайте разберемся в каждом из них и выясним, как лучше всего реализовать клонирование в наших Java-программах.

1. Неглубокое Клонирование

По умолчанию реализация метода клонирования объектов Java() использует неглубокое копирование. Он использует API отражения для создания копии экземпляра. Приведенный ниже фрагмент кода демонстрирует реализацию неглубокого клонирования.

@Override
 public Object clone() throws CloneNotSupportedException {
 
	 Employee e = new Employee();
	 e.setId(this.id);
	 e.setName(this.name);
	 e.setProps(this.props);
	 return e;
}

2. Глубокое Клонирование

При глубоком клонировании мы должны копировать поля одно за другим. Если у нас есть поле с вложенными объектами, такими как Список, карта и т.д., То мы должны написать код, чтобы скопировать их тоже по одному. Вот почему это называется глубоким клонированием или глубоким копированием.

Мы можем переопределить метод клонирования сотрудника, как показано в следующем коде для глубокого клонирования.

public Object clone() throws CloneNotSupportedException {

	Object obj = super.clone(); //utilize clone Object method

	Employee emp = (Employee) obj;

	// deep cloning for immutable fields
	emp.setProps(null);
	Map hm = new HashMap<>();
	String key;
	Iterator it = this.props.keySet().iterator();
	// Deep Copy of field by field
	while (it.hasNext()) {
		key = it.next();
		hm.put(key, this.props.get(key));
	}
	emp.setProps(hm);
	
	return emp;
}

При реализации этого метода clone() наша тестовая программа выдаст следующий результат.

emp and clonedEmp == test: false
emp and clonedEmp HashMap == test: false
clonedEmp props:{city=Bangalore, salary=10000}
clonedEmp name:Pankaj

В большинстве случаев это то, чего мы хотим. Метод clone() должен возвращать новый объект, полностью отделенный от исходного экземпляра.

Поэтому, если вы думаете использовать клонирование объектов и клонирование в своей программе, делайте это с умом и правильно переопределяйте его, заботясь о изменяемых полях.

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

Клонирование с использованием сериализации?

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

Использование утилиты Apache Commons

Если вы уже используете классы Apache Commons Util в своем проекте и ваш класс сериализуем, используйте приведенный ниже метод.

Employee clonedEmp = org.apache.commons.lang3.SerializationUtils.clone(emp);

Конструктор копирования для клонирования

Мы можем определить конструктор копирования для создания копии объекта. Зачем вообще зависеть от метода клонирования объекта ()?

Например, у нас может быть конструктор копирования сотрудников, подобный следующему коду.

public Employee(Employee emp) {
	
	this.setId(emp.getId());
	this.setName(emp.getName());
	
	Map hm = new HashMap<>();
	String key;
	Iterator it = emp.getProps().keySet().iterator();
	// Deep Copy of field by field
	while (it.hasNext()) {
		key = it.next();
		hm.put(key, emp.getProps().get(key));
	}
	this.setProps(hm);

}

Всякий раз, когда нам нужна копия объекта employee, мы можем получить ее с помощью Employee Employee(emp); .

Однако написание конструктора копирования может быть утомительной работой, если в вашем классе много переменных, особенно примитивных и неизменяемых.

Рекомендации по клонированию объектов Java

  1. Используйте метод клонирования объекта по умолчанию() только в том случае, если в вашем классе есть примитивы и неизменяемые переменные или вы хотите мелкое копирование. В случае наследования вам придется проверить все классы, которые вы расширяете до уровня объекта.
  2. Вы также можете определить конструктор копирования, если ваш класс имеет в основном изменяемые свойства.
  3. Используйте метод клонирования объекта (), вызвав super.clone() в переопределенном методе клонирования, затем внесите необходимые изменения для глубокого копирования изменяемых полей.
  4. Если ваш класс сериализуем, вы можете использовать сериализацию для клонирования. Однако это приведет к снижению производительности, поэтому проведите сравнительный анализ, прежде чем использовать сериализацию для клонирования.
  5. Если вы расширяете класс и он правильно определил метод клонирования с помощью глубокого копирования, вы можете использовать метод клонирования по умолчанию. Например, мы правильно определили метод clone() в классе Employee следующим образом.

    Мы можем создать дочерний класс и использовать глубокое клонирование суперкласса следующим образом.

    Класс Employee Wrap не имеет никаких изменяемых свойств и использует реализацию метода клонирования суперкласса (). Если есть изменяемые поля, то вам придется позаботиться о глубоком копировании только этих полей.

    Вот простая программа, чтобы проверить, работает ли этот способ клонирования нормально или нет.

    Выход:

    Так что все сработало идеально, как мы и ожидали.

Это все о клонировании объектов в java. Я надеюсь, что у вас есть некоторое представление о методе клонирования объектов Java() и о том, как правильно переопределить его без каких-либо негативных последствий.

Ссылка: Документ API для клонирования объекта