В чем разница между глубоким и поверхностным клонированием в Java
Что проверяют
Понимаете ли вы, что копируется при клонировании: сам объект или ещё и всё, на что он ссылается. Следом почти всегда спрашивают про Cloneable и почему в реальном коде clone() стараются не использовать.
Как отвечать
- поверхностная копия: новый объект, у которого поля скопированы как есть. Примитивы получают свои значения, а ссылочные поля указывают на те же объекты, что и в оригинале. Именно так работает
Object.clone(): копирует поля один в один и не вызывает конструктор; - глубокая копия: вложенные изменяемые объекты тоже копируются, рекурсивно, так что оригинал и копия больше не делят состояние.
Механика clone(): метод объявлен в Object как protected, и если класс не реализует маркерный интерфейс Cloneable, вызов бросит CloneNotSupportedException. При переопределении его делают public и сужают тип возврата: public Person clone(). Для глубокой копии после super.clone() вручную копируют изменяемые поля: copy.address = address.clone(), copy.tags = new ArrayList<>(tags). Неизменяемые поля вроде String или LocalDate копировать не нужно.
Хороший ответ заканчивается альтернативами. Конструктор копирования или статическая фабрика Person.copyOf(p) явны, работают с final полями и не требуют Cloneable, это же советует «Effective Java». Сериализация туда и обратно (стандартная или через Jackson) даёт глубокую копию одной строкой, но работает медленно и требует, чтобы сериализовался весь граф. Лучший вариант часто вообще не копировать: неизменяемые объекты и record можно раздавать без опаски.
Где спотыкаются
Считают копию коллекции глубокой. new ArrayList<>(list) и ArrayList.clone() создают новый список, но элементы в нём те же самые объекты.
Забывают про final поля. Внутри clone() им нельзя присвоить новое значение, поэтому глубокая копия класса с изменяемым final полем через clone() не делается.
Пишут рекурсивное копирование без учёта циклических ссылок. Граф, где объекты ссылаются друг на друга, уводит такую копию в бесконечную рекурсию и StackOverflowError, нужен словарь уже скопированных объектов.