자바를 처음 공부하면서 call-by-value와 call-by-reference에 관한 궁금증이 생겼다.
이에 관해 공부한 기록이다.
Call-by-value vs Call-by-reference
두 개념은 어떻게 다를까?
두 개념 모두 변수가 호출되는 방식에 관한 개념이다.
- Call-by-value는 호출자와 피호출자가 각각 독립적인 두 변수를 가진다.
- Call-by-reference는 호출자와 피호출자가 동일한 변수를 사용한다.
호출자와 피호출자가 동일한 변수를 사용한다는 것은 조금 이해하기 힘든 표현일 수 있다.
그 이유는 Call-by-reference라는 개념 자체가 포인터와 결부되어 있기 때문이다.
내가 이해하기로는 Call-by-value는 그릇에 비유될 수 있다.
Stack 메모리에 변수가 쌓이고 그 변수에는 값이 담긴다(할당).
때문에 해당 변수가 호출된다면(피호출자가 된다면) 변수를 호출한 호출자(지역 변수)에 동일한 값이 담기는 것이다.
하지만 Call-by-referenc는 보다 추상적인 개념으로, 변수보다는 해당 주소에 담기는 것으로 이해했다.
이는 메모리에 직접 접근할 수 있기 때문에 실현될 수 있는 개념으로
특정 주소의 값을 변수라는 그릇에 담는 것이 아닌 특정 주소에 들어갈 값을 지정하는 방법인 것이다.
Call-by-value ~ Reference type
- Reference type은 Heap 메모리에 생성된 객체나 배열 등을 가리키는 데이터 타입이다.
- Call-by-value는 특정 변수가 가지는 값이 호출됐을 때 호출자가 해당 값의 복사본을 할당받는 방식이다.
이름과 동작 방식만 봤을 때는 Reference type이 Call-by-reference로 동작하는 것이 자연스러워 보인다.
하지만 위에서 언급한 대로 Call-by-reference는 메모리에 직접 접근해 특정 메모리에 들어갈 값을 지정해야 한다.
예제를 보자
public class CallByReference {
public static void main(String[] args){
int[] arr={1,2,3};
callByReference(arr);
for(int i:arr)System.out.println(i);
}
public static void callByReference(int[] arr){
arr=new int[]{3,2,1};
}
}
Call-by-reference로 동작한다면 새로 생성된 {3,2,1} 배열이 출력되어야 한다.
하지만 실행결과는 피호출자인 {1,2,3}이 출력된다.
그렇다면 반대로 일반적인 Call-by-value로 동작한다고 가정해 보자.
Call-by-value로 동작한다면 호출자와 피호출자가 완전히 독립된 두 변수여야 한다.
따라서 호출자가 피호출자의 본래 값에 영향을 줄 수 없어야 한다는 것을 의미한다.
public class CallByValue {
public static void main(String[] args){
int[] arr={1,2,3};
callByValue(arr);
for(int i:arr)System.out.println(i);
}
public static void callByValue(int[] arr){
arr[0]=10;
}
}
그렇다면 위의 코드의 출력값은 arr가 callByValue 메서드의 영향을 받지 않았기 때문에 {1,2,3}이 출력되어야 한다.
하지만 출력값은 메소드의 영향을 받은 {10,2,3} 배열이 출력된다.
그렇다면 도대체 어떤 방식으로 동작하는 걸까?
Call-by-sharing
위의 상황을 정리해 보면,
- 호출자에 새로운 값이 할당되었을 때 피호출자에 영향을 주지 않기 때문에 Call-by-reference는 아니다.
- 호출자는 피호출자와 같은 배열을 가리키고 있다. 따라서 호출자에서 배열에 접근해서 배열 내부를 변경시키면,
피호출자에도 그 수정이 반영된다.
따라서 아래가 참인 것을 알 수 있다.
호출자와 피호출자가 Reference type일 때, 같은 주소값을 가진다.
객체의 복사본을 가지는 것이 아닌 같은 주소값을 value로 한 Call-by-value 방식인 것이다.
그렇게 되면 위 두 상황이 설명이 된다.
예제를 보자
public class CallBySharing {
public static void main(String[] args){
int[] arr={1,2,3};
callBySharing(arr);
for(int i:arr)System.out.println(i);
//10 2 3
}
public static void callBySharing(int[] arr){
arr[0]=10;
//같은 배열을 가리키는 시점이므로 피호출자의 첫 번째 원소도 10으로 바뀜
arr=new int[] {3,2,1};
//서로 다른 배열을 가리키게 됨
arr[0]=10;
//피호출자는 영향을 받지 않음
}
}
이처럼 호출자가 피호출자의 값을 복사해서 두 변수가 완전히 독립적으로 동작하는 일반적인 Call-by-value 방식이 아닌
호출자가 피호출자에 담긴 주소값을 복사해서 같은 객체를 가리키는 방식을 Call-by-sharing이라고 한다.
참고
'JAVA' 카테고리의 다른 글
[JAVA] 상속과 조합 (1) | 2023.11.08 |
---|---|
[JAVA] VO(Value Object), 원시값 포장 (0) | 2023.10.28 |
[JAVA] equals(), hashCode() 재정의(동등성과 동일성) (0) | 2023.10.27 |
[JAVA] JVM 메모리 영역 (0) | 2023.06.13 |