Deep Copy, Shallow Copy / Call by value

Dayeon myeong·2022년 3월 13일
0

면접

목록 보기
14/35

Call by value, Call by reference

Call by value : 원본 값을 변경하지 않고, 값을 복사해서 새로운 함수로 넘기는 호출 방식.primitive type의 경우에는 값이 복사되어 매개변수로 전달되고, reference type( 객체 타입 )의 경우에는 객체의 heap reference 주소값이 복사되어 매개변수로 전달된다.

Call by reference : 값을 직접 참조한다. 즉 함수로 전달된 값이 변경할 경우 원본도 같이 변경된다.

자바는 Call by value뿐이다 : primitive type

public class PassPrimitiveByValue {

    public static void main(String[] args) {
           
        int x = 3;
           
        // invoke passMethod() with 
        // x as argument
        passMethod(x);
           
        // print x to see if its 
        // value has changed
        System.out.println("After invoking passMethod, x = " + x);
           
    }
        
    // change parameter in passMethod()
    public static void passMethod(int copyX) {
        copyX = 10;
    }
}
After invoking passMethod, x = 3

Call by value : 원본 값을 변경하지 않고, 값을 복사해서 새로운 함수로 넘기는 호출 방식.primitive type의 경우에는 값이 복사되어 매개변수로 전달되고, reference type( 객체 타입 )의 경우에는 객체의 heap reference 주소값이 복사되어 매개변수로 전달된다.

int, double 과 같은 Primitive type의 매개변수들은 call by value이다. 즉, 값이 복사되어 매개변수로 넘어간다.
x = 3인 애가 있고, 값이 복사된 copyX = 3인 애가 매개변수로 넘어간다. 메서드 내에서 copyX = 10이 되고 메서드의 스코프는 메서드내에만 존재하기 때문에 메서드가 리턴되면서 copyX = 10은 사라진다. 원본값 x = 3은 그대로 유지된다. 즉 call by value는 메서드 내부에서 값을 변경하더라도 원본값에 영향을 끼치지 않는다.

자바는 Call by value뿐이다 : reference type

public void moveCircle(Circle copyCircle, int deltaX, int deltaY) {
    // code to move origin of circle to x+deltaX, y+deltaY
    copyCircle.setX(circle.getX() + deltaX);
    copyCircle.setY(circle.getY() + deltaY);
        
    // code to assign a new reference to circle
    copyCircle = new Circle(0, 0);
}
Circle myCircle = new Circle(0,0);
moveCircle(myCircle, 23, 56)

Call by value : 원본 값을 변경하지 않고, 값을 복사해서 새로운 함수로 넘기는 호출 방식.primitive type의 경우에는 값이 복사되어 매개변수로 전달되고, reference type( 객체 타입 )의 경우에는 객체의 heap reference 주소값이 복사되어 매개변수로 전달된다.

자바는 객체들도 무조건 call by value이다. 다만 객체의 주소값(heap 영역의 reference 주소값)이 복사되어 넘어가진다.

만약 heap reference가 32인 myCircle 이란 객체를 moveCircle 메서드의 인자로 넘겨준면,
heap reference 32가 복사된 copyCircle이란 매개변수가 넘어간다.

setX나 setY을 할 때에는 복사된 heap reference 32가 가르키는 실제 객체의 멤버 변수를 수정가능하다. 따라서 myCircle은 x= 23, y = 56이 된다.

copyCircle = new Circle(0,0);

위 문장에서는 copyCircle 에 새로운 객체가 heap 영역에 생성되면서 만약 이 새로운 객체의 heap reference가 50이면 copyCircle은 해당 50이라는 heap reference를 가르키게 된다.
메서드가 끝나면 메소드 스코프에 벗어나기 때문에 copyCircle은 사라지게 된다.

깊은 복사, 얕은 복사

얕은 복사 Shallow copy : heap reference(객체의 주소값)을 복사한다. 따라서 원본에 영향을 줄 수 있다.
깊은 복사 Deep Copy : 새로운 메모리 공간을 만들어서 실제 값을 복사한다.

얕은 복사 Shallow copy

Jack jack = new Jack("jack", 10000);
Jack jackShallowCopy = jack; // 얕은 복사

jack.changeName("changeJack");
jack.spand(3000);

System.out.println(jack); // name = changeJack, money = 7000
System.out.println(jackShallowCopy); //name = changeJack, money = 7000

위 코드는 얕은 복사의 예시이다.
얕은 복사는 heap reference(객체의 주소값)을 복사한다. 따라서 원본에 영향을 줄 수 있다.

처음 jack 객체를 생성 시 heap reference가 30이라고 하자. jackShallowCopy에 =를 통해 얕은 복사가 되면 jackShallowCopy는 heap reference 30을 복사한다.

이후에 이름을 변경하고, 3000원을 쓰면
jack 객체가 가르키는 heap reference 30에 있는 실제 객체의 값을 수정한다. jackShallowCopy도 이 heap reference 30을 가르키니 똑같이 값이 변경된 채로 출력된다.

깊은 복사 Deep copy

Jack jack = new Jack("jack", 10000);
Jack jackDeepCopy = new Jack(); 
jackDeepCopy.setName(jack.getName());//필드 하나하나 복사
jackDeepCopy.setMoney(jack.getMoney());

jack.changeName("changeJack");
jack.spand(3000);

System.out.println(jack); // name = changeJack, money = 7000
System.out.println(jackDeepCopy); //name = jack, money = 10000

깊은 복사는 새로운 메모리 공간을 만들어서 실제 값을 복사한다.

jack 객체를 만든후, jack.clone() 메서드를 통해 깊은 복사를 하면 heap 영역에 새로운 객체가 메모리에 올라오고 jack, 10000이라는 멤버 변수들 정보가 그대로 복사된다. 기존 jack과 무관하게 새로운 jackDeepCopy 객체가 있는 것이다.

참고

https://jackjeong.tistory.com/100

Java는 Call By Reference일까, Call By Value일까?

Passing Information to a Method or a Constructor

[Java] Java는 Call by reference가 없다

profile
부족함을 당당히 마주하는 용기

0개의 댓글