본문으로 바로가기

자바 Clone에 대해서

category Java 개발 이야기 2018. 10. 4. 19:30
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.



clone() 이 메서드는 새로운 특정 클래스를 복제하여 새로운 인스턴스를 생성하려고 할 때 사용한다. clone()을 사용하면 이전의 값은 보존되고, 작업에 실패해서 원래의 상태로 되돌리거나 변경하기 전의 값을 참고하는데 도움이 된다.

clone은 Object 클래스에 정의가 되어 있는데 단순히 인스턴스의 값을 복사하기 때문에 참조 변수 타입의 인스턴스 변수가 정의되어 있는 클래스는 완전한 인슨턴스 복제가 이루어지지 않는다.


clone() 을 사용하려면 복사하려는 클래스의 Cloneable를 implements 해주어야 한다. 그리고 clone() 메서드를 재정의 해주면 된다.


아래 예제 코드를 보면 27번 라인에 p1.clone() 를 호출하여 객체를 복사했음을 알 수 있다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class Point implements Cloneable {
    int x;
    int y;
    
    Point(int x, int y) {
        this.x = x;
        this.y = y;
    }
    
    @Override
    protected Point clone() {
        Object obj = null;
        try {
            obj = super.clone();
        } catch (CloneNotSupportedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        
        return (Point) obj;
    }
}
 
public class Test {
    public static void main(String[] args) {
        Point p1 = new Point(12);
        Point p2 = (Point) p1.clone();
        p2.x = 3; p2.y = 4;
        
        System.out.println(p1.x + " : " + p1.y); //1 : 2
        System.out.println(p2.x + " : " + p2.y); //3 : 4
        System.out.println(p1 == p2);  //false
        System.out.println(p1.equals(p2)); //false
    }
}



1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static void main(String[] args) {
    int[] arr = { 1,2,3,4,5 };
    int[] arrClone = arr.clone();
    
    System.out.println(arr.hashCode());          //366712642
    System.out.println(arrClone.hashCode());     //1829164700
    
    System.out.println(arr);          //[I@15db9742
    System.out.println(arrClone);     //[I@6d06d69c
    
    arrClone[0= 9;
    System.out.println(Arrays.toString(arr));          //[1, 2, 3, 4, 5]
    System.out.println(Arrays.toString(arrClone));     //[9, 2, 3, 4, 5]
}



얕은 복사와 깊은 복사


clone은 단순히 객체의 값에 저장된 값을 복제할 뿐, 객체 안에 참조되는 객체를 복제하지는 않는다.

아래 예제를 보자. Man 클래스 아래에 Info 클래스 자료형인 info 변수가 포함되어 있다. 그렇다면 복사한 객체의 값 안에 Info 변수를 변경했을 때 두 사이의 값은 변경한 값으로 둘 다 바뀌어 있다. 이건 심각한 문제이다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
    class Info {
        private String birthDay;
        private String age;
    
        public Info(String birthDay, String age) {
            this.birthDay = birthDay;
            this.age = age;
        }
    
        public String getBirthDay() {
    
            return birthDay;
        }
    
        public void setBirthDay(String birthDay) {
    
            this.birthDay = birthDay;
        }
    
        public String getAge() {
    
            return age;
        }
    
        public void setAge(String age) {
    
            this.age = age;
        }
    }
    
    class Man implements Cloneable {
        private String name;
        private Info info;
 
        public String getName() {
 
            return name;
        }
 
        public void setName(String name) {
 
            this.name = name;
        }
 
        public Info getInfo() {
 
            return info;
        }
 
        public void setInfo(Info info) {
 
            this.info = info;
        }
 
        public Man copy() {
            Man ref = null;
 
            try {
                ref = (Man) this.clone();
            } catch (CloneNotSupportedException e) {
                e.printStackTrace();
            }
 
            return ref;
        }
 
    }
 
    public class Test {
        public static void main(String[] args) {
            Man ktko = new Man();
            ktko.setName("ktko");
            ktko.setInfo(new Info("19890714""30"));
            
            Man kkt = ktko.copy();
            kkt.setName("kkt");
            kkt.getInfo().setBirthDay("19900714");
            kkt.getInfo().setAge("29");
            
            System.out.println(ktko.getName());                    //ktko
            System.out.println(kkt.getName());                     //kkt
            
            System.out.println(ktko.getInfo().getBirthDay());     //19900714
            System.out.println(ktko.getInfo().getAge());         //29
            System.out.println(kkt.getInfo().getBirthDay());     //19900714
            System.out.println(kkt.getInfo().getAge());            //29
        }
    }



이럴 때 copy 함수를 변경해보자


1
2
3
4
5
6
7
8
9
10
11
12
public Man copy() {
    Man ref = null;
 
    try {
        ref = (Man) super.clone();
        ref.setInfo(new Info(this.info.getBirthDay(), this.info.getAge()));
    } catch (CloneNotSupportedException e) {
        e.printStackTrace();
    }
 
    return ref;
}



이렇게 copy함수 안에 복사된 객체를 return 하게 되면 깊은 복사를 할 수 있다.