programowanieProgramista Java

Wyjaśnij różnice między shallow copy a deep copy w Javie. Jak są one realizowane i kiedy może zaistnieć potrzeba ich użycia?

Zdaj rozmowy kwalifikacyjne z asystentem AI Hintsage

Odpowiedź

Shallow copy (płytkie kopiowanie) tworzy nowy obiekt, ale kopiuje referencje do obiektów zagnieżdżonych, a nie same obiekty. Deep copy (głębokie kopiowanie) tworzy nowy obiekt i rekurencyjnie kopiuje wszystkie obiekty zagnieżdżone w oryginalnym, w ten sposób między kopią a oryginałem nie ma wspólnych referencji.

Kiedy używać:

  • Shallow copy jest wystarczające, jeśli struktura obiektu nie zawiera zmiennych obiektów zagnieżdżonych, lub jeśli nie ma potrzeby zmieniania zagnieżdżonych obiektów.
  • Deep copy jest konieczne, jeśli zmienność zagnieżdżonych obiektów jest krytyczna, a jakiekolwiek modyfikacje kopii nie powinny być odzwierciedlane w oryginale.

Przykład realizacji:

// Przykład klasy z zagnieżdżonym obiektem class Address { String city; Address(String city) { this.city = city; } } class Person implements Cloneable { String name; Address address; Person(String name, Address address) { this.name = name; this.address = address; } // Shallow copy public Person clone() throws CloneNotSupportedException { return (Person) super.clone(); } // Deep copy public Person deepClone() { return new Person(this.name, new Address(this.address.city)); } }

Pytanie z pułapką

Czy można zrealizować deep copy za pomocą metody clone()?

Odpowiedź: Nie, standardowa metoda Object.clone() realizuje tylko "shallow copy". Aby uzyskać deep copy, trzeba albo ręcznie sklonować wszystkie zagnieżdżone pola, albo skorzystać z zewnętrznych bibliotek (na przykład Apache Commons Lang).

Przykład:

@Override public Person clone() throws CloneNotSupportedException { Person cloned = (Person) super.clone(); cloned.address = new Address(this.address.city); // deep copy ręcznie return cloned; }

Historia

W dużym projekcie CRM, po wdrożeniu kopiowania obiektów przez super.clone(), programista oczekiwał całkowitej niezależności kopii, ale zmieniając adres skopiowanego użytkownika, zmieniał się również adres oryginału. Błąd ujawniono dopiero po wydaniu i spowodował zamieszanie w bazie klientów.


Historia

Podczas serializacji/deserializacji do głębokiego kopiowania wybrano standardowy mechanizm Serializable. Jednak zapomniano zadeklarować jedno z pól zagnieżdżonego obiektu jako Serializable, co doprowadziło do błędów podczas pracy (NotSerializableException) i utraty danych podczas tworzenia kopii zapasowej.


Historia

W jednym z mikrousług używano biblioteki kopiowania BeanUtils.copyProperties do kopiowania DTO, nie zdając sobie sprawy, że mechanizm realizuje tylko shallow copy. Po refaktoryzacji pojawiło się wiele nieoczywistych błędów, gdy zmiana zagnieżdżonych kolekcji wpływała na oryginalne dane pobrane z bazy.