본문 바로가기

Java

clone 메서드의 문제점 (얕은 복사, 깊은 복사)

clone메서드는 Object클래스의 메서드로 해당 객체의 복사본을 리턴해 주는 메서드이다.

해당 메서드의 문제점은 값에 의한 복사가 이루어진다는 것이다.

값에 의한 복사란 인스턴스의 멤버들의 값들이 복사가 된다는 것이다. 이건 멤버들이 전부 참조변수가 아니면 문제가 되지 않는다.  만약 참조변수 포함하고 있다면 참조변수 안의 주소가 복사된 가 때문에 복사된 인스턴스가 원본인스턴스와 같은 주소를 가리키게 된다는 문제점이 발생한다.(얕은 복사) 이를 원치 않다면 clone()의 오버라이딩이 필요한 시점이다.

 

※ 해당 class가 clone()을 지원하는지 확인하는

방법은 해당 class가 Cloneable 인터페이스를 구현한 클래스인지를 확인하면 된다. 

 

얕은 복사 예제

package myColne;

import java.util.Arrays;

// clone을 사용하려면 Cloneable인터페이스를 구형해야 한다.
// 인스턴스의 복제를 허용한다는 의미이다.
public class Myclone implements Cloneable {
	private int[] arr;
	
	public Myclone(){;}
	public Myclone(int a) {
		super();
		arr = new int[a];
	}

	
	public int[] getArr() {
		return arr;
	}
	
	public void setArr(int[] arr) {
		for (int i = 0; i < this.arr.length; i++) {
			this.arr[i] = arr[i];
		}
	}
	
	public boolean alterArr(int index, int value) {
		boolean condition = arr.length > index && index >= 0; 
		boolean Return = false;
		if (condition) {
			Return = true;
			this.arr[index] = value;
			return Return;
		}
		return Return;
	}
	
	@Override
//	공변타입이란 오버라이딩하는 메서드의 리턴타입을 부모타입이 아닌 자손 타입으로 변경이 가능하게 해 주는 것이다.
//	JDK1.5부터 지원 
	public  Myclone clone() {
		Object object = null;
		try {
			object = super.clone();
			// try - catch 문 필요 
		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
		return  (Myclone)object;		 
	}
	
	
	public Myclone deepClone() {
		// 값에의한 복사 진행
		Myclone copy = this.clone();
		// 참조변수에 대한 부분만 수정 
		copy.arr = this.arr.clone();
		return copy;
	}
	
	@Override
	public String toString() {
		return "[arr=" + Arrays.toString(arr) + "]";
	}
	
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + Arrays.hashCode(arr);
		return result;
	}
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Myclone other = (Myclone) obj;
		return Arrays.equals(arr, other.arr);
	}

}

 

public class Test {
	public static void main(String[] args) {
		
		Myclone myclone = new Myclone(10);
		Myclone copy = null; 
		int[] arr = {1, 4, 5, 6, 7, 54611, 661, 77, 0, 33, 617, 10, 85, 90}; 
		myclone.setArr(arr);
		
		// 얕은 복사 
		copy = myclone.clone();
		
		// 확인
		// 배열의 값이 같다.
		System.out.println("배열의 값 : " + myclone);
		System.out.println("배열의 값 : " + copy);
		
		// 다른 배열을 가리킨다.
		System.out.println("해시값 : " + myclone.getArr());
		System.out.println("해시값 : " + copy.getArr());
		
		// 문제점
		// 복사본의 참조변수를 변경하면 원본의 값까지 변경된다. 
		copy.alterArr(0, 100);	
		System.out.println("배열의 값 : " + myclone);
		System.out.println("배열의 값 : " + copy);	
		
	}
}

main의 마지막 코드를 보면 copy를 변경해도 원본까지 변경되는 문제점이 발생한다.

 

해결방법

clone()을 오버라이딩 할 때 copy에 새로운 배열을 생성해 주고 값을 복사해 준다. 

(Myclone은 변경되지 않았다.)

public class Test {
	public static void main(String[] args) {
		
		Myclone myclone = new Myclone(10);
		Myclone copy = null; 
		int[] arr = {1, 4, 5, 6, 7, 54611, 661, 77, 0, 33, 617, 10, 85, 90}; 
		myclone.setArr(arr);
		
		// 깊은 복사
		copy = myclone.deepClone();
		
		// 확인
		// 배열의 값이 같다.
		System.out.println("배열의 값 : " + myclone);
		System.out.println("배열의 값 : " + copy);
		
		// 다른 배열을 가리킨다.
		System.out.println("해시값 : " + myclone.getArr());
		System.out.println("해시값 : " + copy.getArr());
		
		// copy의 배열만 변경되었다.	
		copy.alterArr(0, 100);	
		System.out.println("배열의 값 : " + myclone);
		System.out.println("배열의 값 : " + copy);	
		
	}
}

 

'Java' 카테고리의 다른 글

[JAVA] getBytes()와 String생성자로 인코딩 디코딩하기  (0) 2023.06.13
getClass()  (0) 2023.06.09
자바 내 코드 성능 테스트 방법  (1) 2023.06.06
String Class의 특징  (0) 2023.06.05
Object class의 메서드  (0) 2023.05.30