본문 바로가기
프로그래밍/JAVA

Java 깊은 복사 vs 얕은 복사 완벽 정리 (+ 예제 코드)

by 곰 옥수수 2025. 3. 3.
728x90
반응형

얕은 복사(Shallow Copy)와 깊은 복사(Deep Copy)의 개념

  • 얕은 복사: 객체의 주소만 복사하여 동일한 객체를 공유
  • 깊은 복사: 참조 객체도 새롭게 생성하여 원본과 독립적인 객체로 복제
  •  

얕은 복사(Shallow Copy)

  • 객체를 복사할 때,  주소값만 복사하는 방식
  • 원본 객체와 복사된 객체가 같은 참조값을 공유하게 되어 한쪽에서 값을 변경하면 다른 쪽에도 영향을 미침

🔹 얕은 복사 예제

class Person {
    String name;
    
    Person(String name) {
        this.name = name;
    }
}

class ShallowCopyTest {
    public static void main(String[] args) {
        Person person1 = new Person("Alice");
        Person person2 = person1; // 얕은 복사 (같은 객체를 가리킴)
        
        person2.name = "Bob"; // person2의 값을 변경하면 person1도 영향을 받음

        System.out.println(person1.name); // Bob
        System.out.println(person2.name); // Bob
    }
}

 

➡ person1과 person2가 동일한 객체를 가리키므로, 하나를 변경하면 다른 객체도 영향을 받음


깊은 복사(Deep Copy)

  • 새로운 객체를 생성한 후, 원본 객체의 모든 필드 값을 복사하지만 참조형 변수(객체 타입 변수)도 새로운 객체로 생성하는 방식
  • 원본 객체와 복사된 객체는 완전히 독립적인 객체가 됨
  • 따라서 한쪽을 변경해도 다른 쪽에는 영향을 미치지 않음

아래 예시들은 참조 데이터(객체)를 깊은 복사하는 예제를 해 볼거다. 

 

※List, Map의 깊은 복사는 아래 링크로 이동

 

Java 리스트(List), Map 의 깊은 복사 완벽 정리 (+ 예제 코드)

Java에서 Map, List, String의 깊은 복사 방법Java에서 Map, List, String을 깊은 복사하는 방법을 살펴보겠습니다.1. ArrayList 깊은 복사방법 1: for 루프 사용 (가장 기본적인 방법)List deepCopy = new ArrayList();for (I

bears-paw.tistory.com

 


1. 수동 복사 (Manual Deep Copy)

  • 복사 생성자를 사용하여 객체 내부의 참조 타입 필드도 새롭게 생성하여 복사
class Address {
    String city;
    
    Address(String city) {
        this.city = city;
    }
}

class Person {
    String name;
    Address address; // 참조 타입 필드

    Person(String name, Address address) {
        this.name = name;
        this.address = address;
    }

    // 깊은 복사 생성자
    Person(Person original) {
        this.name = original.name;
        this.address = new Address(original.address.city); // 새로운 객체 생성
    }
}

class DeepCopyTest {
    public static void main(String[] args) {
        Person person1 = new Person("Alice", new Address("Seoul"));
        Person person2 = new Person(person1); // 깊은 복사

        person2.name = "Bob";
        person2.address.city = "Busan"; // 원본 객체의 값은 변경되지 않음

        System.out.println(person1.name + " - " + person1.address.city); // Alice - Seoul
        System.out.println(person2.name + " - " + person2.address.city); // Bob - Busan
    }
}

참조 타입 필드(Address)도 새로운 객체를 만들어서 복사했기 때문에 독립적인 객체가 됨


2. clone() 메서드 오버라이딩

  • Cloneable 인터페이스를 구현하고 clone() 메서드를 오버라이딩하여 깊은 복사를 수행
  • Object의 clone() 메서드는 얕은 복사를 수행하기 때문에, 내부의 참조 타입 필드를 직접 복사해야 함
class Address implements Cloneable {
    String city;
    
    Address(String city) {
        this.city = city;
    }
    
    // 깊은 복사
    @Override
    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;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Person cloned = (Person) super.clone(); // 얕은 복사
        cloned.address = (Address) this.address.clone(); // 깊은 복사
        return cloned;
    }
}

class DeepCopyTest {
    public static void main(String[] args) throws CloneNotSupportedException {
        Person person1 = new Person("Alice", new Address("Seoul"));
        Person person2 = (Person) person1.clone(); // 깊은 복사 수행

        person2.name = "Bob";
        person2.address.city = "Busan"; // 원본 객체에는 영향 없음

        System.out.println(person1.name + " - " + person1.address.city); // Alice - Seoul
        System.out.println(person2.name + " - " + person2.address.city); // Bob - Busan
    }
}

clone()을 이용하여 참조 타입 객체도 새롭게 복사하여 깊은 복사를 수행


3. JSON 변환을 이용한 깊은 복사

  • Jackson, Gson 등의 라이브러리를 사용하여 객체를 JSON으로 변환 후 다시 객체로 변환하면 깊은 복사가 가능
  • 예제: Gson 라이브러리 사용
import com.google.gson.Gson;

class DeepCopyTest {
    public static void main(String[] args) {
        Gson gson = new Gson();
        
        Person person1 = new Person("Alice", new Address("Seoul"));
        String json = gson.toJson(person1); // 객체를 JSON 문자열로 변환
        Person person2 = gson.fromJson(json, Person.class); // JSON 문자열을 객체로 변환 (깊은 복사)

        person2.name = "Bob";
        person2.address.city = "Busan"; // 원본 객체에는 영향 없음

        System.out.println(person1.name + " - " + person1.address.city); // Alice - Seoul
        System.out.println(person2.name + " - " + person2.address.city); // Bob - Busan
    }
}

Gson을 이용하면 쉽게 깊은 복사가 가능


결론

  • 얕은 복사: 객체의 주소만 복사하여 동일한 객체를 공유
  • 깊은 복사: 참조 객체도 새롭게 생성하여 원본과 독립적인 객체로 복제
  • 깊은 복사 방법
    • 수동 복사 (생성자 이용)
    • clone() 메서드 오버라이딩
    • 직렬화(Serialization)
    • JSON 변환(Gson, Jackson 등 활용)

가장 쉬운 방법: Gson, Jackson
안전한 방법: clone(), Serializable
속도가 빠른 방법: 생성자 수동 복사

 

용도에 맞게 잘 사용해보자!

 

728x90
반응형

댓글