PR CENTER

뉴스룸     |     료실

mobile background

PR CENTER

Object.clone()의 얕은 복사와 깊은 복사

관리자
2026-07-06
조회수 12

Object.clone()의 얕은 복사와 깊은 복사

Java 객체 복사 개념과 clone() 사용 시 주의점

1. 개요

  • 원시 타입값 자체가 복사되어 서로 독립적으로 동작한다.
  • 참조 타입은 객체 자체가 아니라 참조값이 복사될 수 있다.
  • Object.clone()은 새 인스턴스를 만들지만, 기본 동작은 필드 단위 얕은 복사이다.
핵심: 새 객체를 만들었다고 해서 내부 참조 객체까지 모두 새로 만들어지는 것은 아니다.

2. 얕은 복사와 깊은 복사

구분복사 대상원본 영향예시
값 복사값 자체없음int, boolean 등 원시 타입
얕은 복사필드 값 또는 참조값공유 객체 수정 시 영향 있음객체 대입, 기본 clone()
깊은 복사내부 객체까지 새로 생성없음Arrays.copyOf(), 복사 생성자


3. 기본 예시

원시 타입은 값이 복사되므로 한쪽을 변경해도 다른 변수에 영향이 없다.

int myScore = 100;
    int yourScore = myScore;
    yourScore = 30;
    System.out.println(myScore); // 100

배열은 참조 타입이다. 단순 대입은 배열 데이터를 새로 만드는 것이 아니라 같은 배열을 가리키게 만든다.

int[] myScores = {100, 90};
    int[] yourScores = myScores;
    yourScores[0] = 30;
    System.out.println(myScores[0]); // 30
위와 같이 같은 객체를 공유하는 복사가 얕은 복사이다.


4. 깊은 복사는 언제 필요한가?

복사본을 수정해도 원본이 변경되면 안 되는 경우 깊은 복사가 필요하다. 배열은 새 배열을 만들고 값을 복사하면 독립적으로 동작한다.

import java.util.Arrays;
    
    int[] myScores = {100, 90};
    int[] yourScores = Arrays.copyOf(myScores, myScores.length);
    yourScores[0] = 30;
    
    System.out.println(myScores[0]);   // 100
    System.out.println(yourScores[0]); // 30
복사 방식결과주의점
얕은 복사바깥 객체는 새로 만들어질 수 있으나 내부 참조 객체는 공유될 수 있음List, Map, 배열, 직접 만든 클래스 필드는 공유 여부 확인 필요
깊은 복사바깥 객체와 내부 객체까지 필요한 범위에서 새로 생성어디까지 복사할지 기준을 정해야 함


5. Object.clone() 핵심

Object.clone()은 객체의 내부 필드를 복사한 새 인스턴스를 반환한다. 하지만 필드가 다른 객체를 참조하고 있다면 그 내부 객체까지 자동으로 복사하지 않는다.

protected native Object clone() throws CloneNotSupportedException;
항목내용
protected외부에서 사용하려면 보통 clone()을 오버라이딩하여 접근 범위를 조정한다.
nativeJava 코드가 아니라 JVM/네이티브 레벨에서 구현된 메서드임을 의미한다.
Cloneable구현하지 않으면 CloneNotSupportedException이 발생한다.
기본 동작새 객체를 만들고 각 필드를 대입하듯 복사한다. 즉 기본은 얕은 복사이다.
주의점mutable 참조 필드는 clone() 내부에서 별도로 복사해야 독립성이 보장된다.


6. Cloneable

  • Cloneable은 메서드 구현이 없는 마커 인터페이스이다.
  • clone() 메서드를 제공하는 인터페이스가 아니라, Object.clone() 호출을 허용한다는 표시 역할을 한다.
  • 모든 배열은 Cloneable을 구현한 것으로 간주된다.
정리: Object.clone() 자체는 깊은 복사가 아니다. 필요한 경우 mutable 필드를 다시 복사해야 한다.


7. 얕은 복사 예시

아래 예시는 DrinkItem을 clone()했지만 내부의 Cola 객체는 새로 복사하지 않는 경우이다. 따라서 원본과 복사본이 같은 Cola 인스턴스를 공유한다.

class DrinkItem implements Cloneable {
        Cola cola;
        String milkTea;
    
        @Override
        public DrinkItem clone() {
            try {
                return (DrinkItem) super.clone();
            } catch (CloneNotSupportedException e) {
                throw new AssertionError(e);
            }
        }
    }
    
    class Cola {
        String pepsiCola;
        String cocaCola;
    
        Cola(String pepsiCola, String cocaCola) {
            this.pepsiCola = pepsiCola;
            this.cocaCola = cocaCola;
        }
    }
DrinkItem myDrink = new DrinkItem();
    myDrink.cola = new Cola("펩시 오리지널", "코카콜라 레몬");
    myDrink.milkTea = "버블밀크티";
    
    DrinkItem yourDrink = myDrink.clone();
    yourDrink.cola.pepsiCola = "펩시 제로";
    
    System.out.println(myDrink.cola.pepsiCola); // 펩시 제로
필드복사 결과영향
cola참조값만 복사되어 같은 Cola 객체를 공유yourDrink에서 수정하면 myDrink에도 영향 있음
milkTeaString은 불변 객체일반적으로 별도 복사 필요 없음
myDrink와 yourDrink는 서로 다른 DrinkItem 객체이지만, 내부의 Cola 객체를 공유하기 때문에 원본까지 변경된다.


8. 깊은 복사로 변경하는 방법

깊은 복사가 필요하면 clone() 결과를 그대로 반환하지 말고, 내부의 mutable 참조 필드를 새 객체로 교체해야 한다. 아래 예시는 복사 생성자로 Cola까지 새로 복사하는 방식이다.

class Cola {
        String pepsiCola;
        String cocaCola;
    
        Cola(String pepsiCola, String cocaCola) {
            this.pepsiCola = pepsiCola;
            this.cocaCola = cocaCola;
        }
    
        Cola(Cola source) {
            this.pepsiCola = source.pepsiCola;
            this.cocaCola = source.cocaCola;
        }
    }
    
    class DrinkItem implements Cloneable {
        Cola cola;
        String milkTea;
    
        @Override
        public DrinkItem clone() {
            try {
                DrinkItem cloned = (DrinkItem) super.clone();
                cloned.cola = new Cola(this.cola);
                return cloned;
            } catch (CloneNotSupportedException e) {
                throw new AssertionError(e);
            }
        }
    }
이제 myDrink.cola와 yourDrink.cola는 서로 다른 Cola 객체이다. 복사본의 cola를 수정해도 원본에는 영향을 주지 않는다.


9. 확인 체크리스트

확인 항목판단 기준
배열, List, Map 필드가 있는가?기본 clone()만으로는 내부 데이터 공유 가능성을 확인해야 한다.
직접 만든 클래스 타입 필드가 있는가?해당 객체가 mutable이면 별도 복사 로직이 필요하다.
String, Integer 같은 불변 객체만 있는가?공유되어도 값 자체가 변경되지 않으므로 문제가 적다.
복사본 수정이 원본에 영향을 주면 안 되는가?깊은 복사 또는 명확한 복사 생성자 사용을 검토한다.


정리

  • 원시 타입은 값 자체가 복사된다.
  • 참조 타입은 참조값이 복사되면 같은 객체를 공유할 수 있다.
  • Object.clone()은 새 객체를 만들지만 기본적으로 얕은 복사를 수행한다.
  • 깊은 복사가 필요하면 mutable 참조 필드를 clone() 내부에서 별도로 복사해야 한다.
한 줄 정리: clone()의 결과가 안전한 복사인지 여부는 내부에 mutable 참조 필드가 있는가로 판단한다.




                                                                                                                                                    ⭐발표자 : 이현진님

0 0

페이지 바로가기

@2024 K2SYSTEMS. All rights reserved.

HOME       |       ABOUT US       |       SOLUTION       |       PR CENTER       |       CONTACT       |       인재채용       |       kakao i cloud 고객센터  

@2024 K2SYSTEMS. All rights reserved.