programowanieProgramista Java

Wyjaśnij różnicę między shallow copy a deep copy obiektów w Javie. Jak można je zaimplementować i w jakich sytuacjach ważne jest zrozumienie tej różnicy?

Zdaj rozmowy kwalifikacyjne z asystentem AI Hintsage

Odpowiedź.

W Javie shallow copy (powierzchowne kopiowanie) to kopiowanie obiektu, w którym kopiowane są tylko odniesienia do obiektów zagnieżdżonych, a nie same obiekty. Tak więc zmiany w obiektach zagnieżdżonych wpłyną zarówno na kopię, jak i na oryginał. Deep copy (głębokie kopiowanie) oznacza tworzenie nowych niezależnych kopii wszystkich zagnieżdżonych obiektów.

Przykład shallow copy (przez clone)

class Address implements Cloneable { String city; Address(String city) { this.city = city; } protected Object clone() throws CloneNotSupportedException { return super.clone(); } } class Person implements Cloneable { String name; Address address; Person(String name, Address address) { this.name = name; this.address = address; } protected Object clone() throws CloneNotSupportedException { return super.clone(); } } // Użycie Person p1 = new Person("Ivan", new Address("Moskwa")); Person p2 = (Person) p1.clone(); p2.address.city = "Petersburg"; System.out.println(p1.address.city); // Wyświetli "Petersburg" — kopiowanie powierzchowne

Przykład deep copy

class Address implements Cloneable { String city; Address(String city) { this.city = city; } protected Object clone() throws CloneNotSupportedException { return new Address(this.city); } } class Person implements Cloneable { String name; Address address; Person(String name, Address address) { this.name = name; this.address = address; } protected Object clone() throws CloneNotSupportedException { Person cloned = (Person) super.clone(); cloned.address = (Address) address.clone(); return cloned; } }

Głębokie kopiowanie pozwala stworzyć całkowicie niezależną kopię obiektu.

Pytanie podchwytliwe.

Pytanie: Czy można użyć metody clone() do gwarantowanego głębokiego kopiowania dowolnego skomplikowanego obiektu?

Odpowiedź: Nie, standardowy clone() wykonuje tylko kopiowanie powierzchowne (shallow copy). Aby uzyskać głębokie kopiowanie, należy nadpisać clone() we wszystkich zagnieżdżonych klasach i jawnie klonować wszystkie pola-odniesienia. Ponadto wiele kolekcji i standardowych klas nie obsługuje głębokiego klonowania.

Przykłady rzeczywistych błędów z powodu braku znajomości szczegółów tematu.


Historia

Kolekcja użytkowników i ich adresów była klonowana metodą clone() bez głębokiego kopiowania. Po zmianie adresu u jednego użytkownika, adres zmienił się również u drugiego, ponieważ kopiowana była referencja. Dane w bazie "popłynęły" między użytkownikami.


Historia

Podczas serializacji skomplikowanego obiektu z zagnieżdżonymi kolekcjami używano clone(), a nie głębokiej kopii. Po przywróceniu z serializacji dane okazały się niespójne i wystąpiły NPE.


Historia

W REST API obiekty DTO były klonowane domyślnie przez BeanUtils.copyProperties(), co prowadziło do podziału tej samej kolekcji między klientami. W wyniku tego użytkownik mógł widzieć i edytować cudze dane.