[Java] 자바 DeepClone을 통해서 클래스를 복사해보자!

2021-01-20


전 글에서 얇은 복제는 단순한 필드 값을 복사할 때만 사용하는 것이 좋으며, 그 이유는 참조 타입을 얇은 복제 시 같은 참조 번지도 같이 복사되기 때문에 복사된 클래스를 변경하면 원본 객체도 변경되게 된다고 했다. 오늘은 이제 이를 해결하는 방법인 DeepClone를 알아보도록 하자. 얇은 복제가 궁금하면 아래 글을 확인하자.

seeminglyjs.tistory.com/232


아래는 참조할 클래스 Student이며 필드로는 학년이 들어갈 grade만 있다.

package dDeepClone;

public class Student {
	int grade;
	
	public Student(int grade) {
		this.grade = grade; // 학생의 학년을 초기화 해줌
	}
	
}

 

다음은 DeepClone을 구현할 예제 클래스인 People이다.

package dDeepClone;

import java.util.Arrays;

public class People implements Cloneable {
	String name;
	int [] bodyinfo; //키와 몸무게가 같이 들어갈 배열
	Student student;
	
	public People(String name, int[] bodyinfo, Student student) {
		this.name = name;
		this.bodyinfo = bodyinfo;
		this.student = student;
	}
	
	@Override
	protected Object clone() throws CloneNotSupportedException {
		
		People cloneP = (People) super.clone();
		//얇은 복제를 먼저해준다. 때문에 super를 이용해 object의 clone메서드를 호출한다.
		//return 값이 object이기 떄문에 People로 감싸주지 않으면 컴파일 오류가 발생한다.
		
		cloneP.bodyinfo = Arrays.copyOf(this.bodyinfo, this.bodyinfo.length);
		//같은 참조번지를 가지고 있는 배열객체를 복사해 새로운 객체로 만들어 주는 과정 
		//Arrays.copyOf의 매개변수는 복사하고자 하는 배열과 해당배열의 길이를 넣어주면 된다.
		
		cloneP.student = new Student(this.student.grade);
		//Student의 클래스 번지를 참조하고 있어 새로운 객체를 this를 이용해 같은 값을 가지는
		//새로운 객체로 선언해 준다.
		
		return cloneP;

	}

	
	public People copyPeople() {
		People cloneP = null;
		//try 구문의 실행실 리턴값이 없을 수 있어
		//초기값을 null로 선언한다.
		
		try {
			cloneP = (People) clone();
		}catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
		return cloneP;
		
	}
	
}

 

자세한 내용은 코드를 보면서 이해하기 쉽도록 모두 주석을 달아두었다. 기존의 ThinClone의 문제점을 해결하기 위해 DeepClone은 이러한 참조객체들을 복사하기 위해 clone() 메서드를 재정의 해준다.

 

이제 해당 내용을 하나씩 살펴보자면 우선 단순히 필드값만 복사하면 되는 얇은 복제의 대상들은 super.clone() (Object의 메서드)로 복사를 진행하면 된다. 이후는 사실 기존 배열이나 클래스를 복사하는 방식과 동일한데 배열은 Arrays.copyOf를 이용해서 동일한 값을 가지고 있는 같은 배열이 만들어질 수 있도록 하며, 클래스 역시 new를 이용해 같은 데이터를 가지는 클래스 객체를 만들어 주면 된다. 

 

메서드 선언은 ThinClone과 동일하게 선언해 주었다. (해당 메서드로 복사를 진행한다.)


package dDeepClone;

public class main {

	public static void main(String[] args) {
			
		People people = new People("abc", new int[] {165, 55}, new Student(3));
		
		People cloneP = people.copyPeople();
		
		cloneP.bodyinfo[0] = 166;
		cloneP.student.grade = 4;
		
		
		System.out.println("복제된 학생의 이름은 = " + cloneP.name);
		System.out.println("복제된 학생의 키와 몸무게는 = " + cloneP.bodyinfo[0] + " / " + cloneP.bodyinfo[1]);
		System.out.println("복제된 학생의 학년은 = " + cloneP.student.grade);
		
		System.out.println("-------------------------------------------------------------------------------");
		
		System.out.println("학생의 이름은 = " + people.name);
		System.out.println("학생의 키와 몸무게는 = " + people.bodyinfo[0] + " / " + people.bodyinfo[1]);
		System.out.println("학생의 학년은 = " + people.student.grade);
		
		
		//출력결과 깊은 복제를 할시 같은 참조번지가 아닌 새롭게 만든 객체의 번지를 참조하여, 기존의 데이터는 기존의 값을
		//유지하는 것을 확인할 수 있다.
		
	}

}

 

 

실행 클래스로 돌아가 클래스 복제후 데이터의 보존성을 확인해 보도록 하자. 해당 부분을 확인하기 위해 복제 클래스의 배열값과 참조 객체의 데이터는 변경하였다.

출력된 값을 확인해보면 원본데이터는 훼손되지 않았다. 복제 클래스의 참조 객체를 변경하여도 기존의 원본 객체는 보존된다는 것을 확인할 수 있다.