Java 배열 clone() 설명: 예제와 모범 사례를 통한 심층 분석
Source: Dev.to
배열에 대한 clone() 메서드가 정확히 무엇인가요?
즉시 기억해야 할 핵심: 배열에 대해 clone()은 얕은 복사를 수행합니다.
얕은 복사 vs. 깊은 복사: 핵심 개념
- 얕은 복사 – 새로운 배열 객체가 생성되지만, 그 요소들은 원본 배열에 저장된 동일한 객체에 대한 참조입니다. 한 배열을 통해 가변 객체를 수정하면 다른 배열에서도 그 변경이 반영됩니다.
- 깊은 복사 – 원본 배열 및 그 안에 포함된 모든 객체를 완전히 독립적인 복제본으로 만듭니다. 하나를 변경해도 다른 쪽에는 영향을 주지 않습니다.
원시 타입 배열(int[], char[], …)의 경우 배열이 실제 값을 저장하므로 구분이 의미가 없습니다. 이 경우 clone()은 사실상 독립적인 복사본을 제공합니다.
객체 배열(String[], Employee[], …)은 참조를 저장하므로 얕은 복사는 그 참조들만 복제합니다.
직접 해보는 예제
예제 1 – 원시 타입 배열
import java.util.Arrays;
public class CloneDemo {
public static void main(String[] args) {
int[] originalScores = {95, 87, 92, 64};
int[] clonedScores = originalScores.clone();
System.out.println("Original Array: " + Arrays.toString(originalScores));
System.out.println("Cloned Array: " + Arrays.toString(clonedScores));
// Change the cloned array
clonedScores[0] = 100;
System.out.println("\nAfter modifying cloned array:");
System.out.println("Original Array: " + Arrays.toString(originalScores)); // Still 95
System.out.println("Cloned Array: " + Arrays.toString(clonedScores)); // Now 100
}
}
출력
Original Array: [95, 87, 92, 64]
Cloned Array: [95, 87, 92, 64]
After modifying cloned array:
Original Array: [95, 87, 92, 64]
Cloned Array: [100, 87, 92, 64]
예제 2 – 함정! (객체 배열)
class Player {
String name;
int level;
Player(String name, int level) {
this.name = name;
this.level = level;
}
}
public class CloneGotcha {
public static void main(String[] args) {
Player[] originalTeam = {
new Player("Alex", 10),
new Player("Sam", 15)
};
Player[] clonedTeam = originalTeam.clone();
System.out.println("Before change:");
System.out.println("Original[0]: " + originalTeam[0].level); // 10
System.out.println("Cloned[0]: " + clonedTeam[0].level); // 10
// Modify the object referenced by the cloned array
clonedTeam[0].level = 99;
System.out.println("\nAfter changing clonedTeam[0].level to 99:");
System.out.println("Original[0]: " + originalTeam[0].level); // Oops! 99!
System.out.println("Cloned[0]: " + clonedTeam[0].level); // 99
}
}
출력
Before change:
Original[0]: 10
Cloned[0]: 10
After changing clonedTeam[0].level to 99:
Original[0]: 99
Cloned[0]: 99
실제 활용 사례: clone()이 빛을 발하는 곳
Getter에서 방어적 복사
public class Configuration {
private String[] settings;
public String[] getSettings() {
return settings.clone(); // Caller cannot modify the internal array
}
}
깊은 복사를 위한 빌딩 블록
clone()은 더 복잡한 깊은 복사 루틴의 첫 단계가 될 수 있으며, 이후 배열에 들어있는 가변 객체들을 수동으로 복사하면 됩니다.
모범 사례 및 피해야 할 함정
- 객체 배열은 얕은 복사라고 가정하세요. 배열이 가변 객체를 담고 있다면
clone()만으로는 충분하지 않은 경우가 많습니다. - 수동 깊은 복사를 수행하세요. 독립적인 객체가 필요할 때는 다음과 같이 구현할 수 있습니다 (예:
Player배열):
Player[] deepCopyTeam = new Player[originalTeam.length];
for (int i = 0; i (original)` or `list.toArray()` for copying.
-
Q: 언제 절대
clone()을 사용하면 안 될까요?
A: 가변 객체를 포함한 배열을 깊게 복사해야 할 때, 혹은 의도를 더 명확히 하고 싶을 때는Arrays.copyOf나 수동 복사와 같은 대안을 사용하는 것이 안전합니다. -
Q: 성능은 어떨까요?
A:clone()은 네이티브 배열 복사이며 얕은 복사에서는 일반적으로 빠릅니다. 깊은 복사는 추가 작업이 필요하므로 복제하는 데이터 양에 비례해 느려집니다.
결론: 사용해야 할까?
clone()을 사용하세요 원시 타입 배열, 불변 객체 배열(String,Integer등) 혹은 얕은 복사만으로 충분할 때.clone()을 피하세요 가변 객체의 독립적인 복사가 필요할 때.- 대안을 고려하세요
Arrays.copyOf,System.arraycopy, 혹은 명시적 루프 등을 사용하면 의도가 더 명확하고 깊은 복사 제어가 용이합니다.
clone()이 어떻게 동작하는지, 그리고 그 한계가 무엇인지 이해하는 것은 올바르고 효율적인 Java 코드를 작성하는 데 필수적입니다.