← Назад к вопросам
В чем разница между клонированием и копированием объектов?
1.3 Junior🔥 201 комментариев
#Основы Java
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
# Разница между клонированием и копированием объектов
Краткий ответ
Копирование - присвоение ссылки одного объекта другой переменной. Обе переменные указывают на один и тот же объект в памяти.
Клонирование - создание нового объекта с теми же значениями полей. Это новый, независимый объект в памяти.
1. Копирование (Reference Copy)
Что происходит: Копируется только ссылка на объект
public class Person {
public String name;
public int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
public static void main(String[] args) {
Person original = new Person("Alice", 30);
Person copy = original; // Копирование ссылки!
System.out.println(original == copy); // true - одинаковые ссылки
System.out.println(System.identityHashCode(original) ==
System.identityHashCode(copy)); // true
// Изменение через одну ссылку видно через другую
copy.name = "Bob";
System.out.println(original.name); // "Bob" - изменилось!
}
Визуально:
Память:
original ──┐
├──→ [Person объект: name="Alice", age=30]
copy ──────┘
Обе переменные указывают на ОДИН объект
Характеристики копирования:
- Две переменные указывают на один объект
- Изменение через одну видно через другую
- Быстро (просто копирует адрес)
- Экономит память
2. Поверхностное клонирование (Shallow Copy)
Что происходит: Создается новый объект, но его поля копируют ссылки исходного объекта
public class Person implements Cloneable {
public String name;
public Address address; // Сложный объект
public Person(String name, Address address) {
this.name = name;
this.address = address;
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone(); // Поверхностное клонирование
}
}
public class Address {
public String city;
public Address(String city) {
this.city = city;
}
}
public static void main(String[] args) throws CloneNotSupportedException {
Address addr = new Address("New York");
Person original = new Person("Alice", addr);
Person clone = (Person) original.clone(); // Поверхностное клонирование
System.out.println(original == clone); // false - разные объекты Person
System.out.println(original.address == clone.address); // true - одинаковые Address!
// Изменение вложенного объекта видно в обоих
clone.address.city = "Boston";
System.out.println(original.address.city); // "Boston" - изменилось!
}
Визуально:
Память:
original ──→ [Person: name="Alice", address ──────┐
↓
clone ────→ [Person: name="Alice", address ──────→ [Address: city="New York"]
Желтые объекты Person разные, но они указывают на ОДИН Address
Характеристики поверхностного клонирования:
- Создается новый объект
- Примитивные типы копируются
- Сложные объекты копируют ссылки
- Проблемы с вложенными объектами
3. Глубокое клонирование (Deep Copy)
Что происходит: Создается новый объект, и все его вложенные объекты тоже клонируются
public class Person implements Cloneable {
public String name;
public Address address;
public Person(String name, Address address) {
this.name = name;
this.address = address;
}
@Override
public Object clone() throws CloneNotSupportedException {
Person cloned = (Person) super.clone(); // Копируем сам Person
cloned.address = (Address) address.clone(); // Клонируем вложенный Address
return cloned;
}
}
public class Address implements Cloneable {
public String city;
public Address(String city) {
this.city = city;
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public static void main(String[] args) throws CloneNotSupportedException {
Address addr = new Address("New York");
Person original = new Person("Alice", addr);
Person clone = (Person) original.clone(); // Глубокое клонирование
System.out.println(original == clone); // false
System.out.println(original.address == clone.address); // false - разные Address!
// Изменение в клоне НЕ влияет на оригинал
clone.address.city = "Boston";
System.out.println(original.address.city); // "New York" - не изменилось!
System.out.println(clone.address.city); // "Boston"
}
Визуально:
Память:
original ──→ [Person: name="Alice", address ──→ [Address: city="New York"]
clone ────→ [Person: name="Alice", address ──→ [Address: city="Boston"]
ВСЕ объекты независимые
Характеристики глубокого клонирования:
- Полная независимость объектов
- Все вложенные объекты тоже клонируются
- Безопасно от побочных эффектов
- Медленнее (клонирует все)
4. Сравнение
| Параметр | Копирование (Reference) | Поверхностное (Shallow) | Глубокое (Deep) |
|---|---|---|---|
| Новый объект | Нет | Да | Да |
| Ссылка на поля | Да | Частично (примитивы копируются) | Нет |
| Независимость | Зависит от другой | Частичная | Полная |
| Скорость | Очень быстро | Быстро | Медленнее |
| Памяти | Минимум | Средне | Много |
| Побочные эффекты | Много | Возможны | Нет |
| Когда использовать | Если нужна одна ссылка | Редко (опасно) | Чаще всего |
5. Практические примеры
Пример 1: Список объектов
List<Person> original = new ArrayList<>();
original.add(new Person("Alice", 30));
original.add(new Person("Bob", 25));
// Копирование ссылки (ПЛОХО)
List<Person> copy1 = original;
copy1.add(new Person("Charlie", 35));
System.out.println(original.size()); // 3 - изменилось!
// Поверхностное копирование (ЛУЧШЕ, но опасно)
List<Person> copy2 = new ArrayList<>(original);
copy2.add(new Person("David", 40));
System.out.println(original.size()); // 2 - не изменилось
// Но если изменить Person внутри copy2, изменится и в original
copy2.get(0).age = 31;
System.out.println(original.get(0).age); // 31 - изменилось!
// Глубокое копирование (БЕЗОПАСНО)
List<Person> copy3 = original.stream()
.map(p -> clonePerson(p))
.collect(Collectors.toList());
Пример 2: Использование Clone
public class Student implements Cloneable {
private String name;
private List<String> courses;
public Student(String name) {
this.name = name;
this.courses = new ArrayList<>();
}
public void addCourse(String course) {
courses.add(course);
}
@Override
public Student clone() {
try {
Student cloned = (Student) super.clone();
cloned.courses = new ArrayList<>(this.courses); // Глубокое копирование списка
return cloned;
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}
}
public static void main(String[] args) {
Student original = new Student("Alice");
original.addCourse("Math");
original.addCourse("Physics");
Student clone = original.clone(); // Глубокое клонирование
clone.addCourse("Chemistry");
System.out.println("Original courses: " + original.courses.size()); // 2
System.out.println("Clone courses: " + clone.courses.size()); // 3
}
6. Альтернативные подходы
Copy Constructor
public class Person {
private String name;
private Address address;
public Person(Person other) {
this.name = other.name;
this.address = new Address(other.address); // Глубокое копирование
}
}
// Использование
Person original = new Person("Alice", new Address("NYC"));
Person clone = new Person(original); // Copy constructor
Builder pattern
Person clone = new Person.Builder(original)
.name(original.getName())
.address(new Address(original.getAddress()))
.build();
JSON сериализация (для глубокого копирования)
import com.fasterxml.jackson.databind.ObjectMapper;
ObjectMapper mapper = new ObjectMapper();
Person original = new Person("Alice", new Address("NYC"));
// Сериализуем в JSON и десериализуем
String json = mapper.writeValueAsString(original);
Person clone = mapper.readValue(json, Person.class); // Глубокое копирование
7. Когда что использовать
Используй копирование ссылки когда:
- Нужны две переменные указывающие на один объект
- Хочешь синхронизировать изменения
- Уверен в своих действиях
Используй поверхностное клонирование когда:
- У объекта только примитивные поля
- НЕ нужна полная независимость
- Редко! (это обычно приводит к багам)
Используй глубокое клонирование когда:
- Нужна полная независимость объектов
- Есть сложные вложенные структуры
- Правило по умолчанию
Заключение
- Копирование - одна ссылка на объект в памяти
- Поверхностное клонирование - новый объект, но вложенные копируют ссылки
- Глубокое клонирование - полностью новый независимый объект
- Используй глубокое клонирование как правило
- Будь осторожен с копированием ссылок в многопоточных приложениях