Call by Value / Call by Reference
Call by Value (값에 의한 호출)
- 값을 복사하여 처리한다.
- 변수의 복사본이 전달되며, 원래 값이 수정되지 않는다.
- 실제 인수는 다른 메모리 위치에 생성된다.
Call by Reference(참조에 의한 호출)
- 값이 주소를 참조하여 직접 값에 영향을 준다.
- 변수 자체가 전달되며, 원래 값이 수정된다.
- 실제 인수는 같은 메모리 위치에 생성된다.
자바의 Call by Value 동작 방식
- 자바의 데이터 타입은 다음과 같이 크게 두 가지로 나누어진다.
- 원시 타입(primitive type) :Numeric Type (byte, short, int, float, long,double, char)Boolean Type (boolean)
- 참조 타입(reference type) :Class Type, Interface Type , Array Type, Enum Type, 기타 참조 타입(String 등)
- 그래서 메소드 파라미터로 원시 타입을 전달하는 것과 참조 타입을 전달하는 것에는 동작 방식에 차이가 있다.
원시 타입(primitive type) 전달 방식
public class CallByExampleTest {
@Test
void primitiveTest() {
int v = 10;
add(v);
assertTat(v).isEqualTo(10);
}
void add(int num) {
num++;
}
}
- 위 코드는 int 타입의 변수 v를 add 함수를 통해 1 더해주고 있다.
- 하지만 변수 v 결과 값은 10 dlek.
- primitiveTest()의 v 변수와 add()의 num은 아예 연관 없는 변수이기 때문이다.
- 자바에서 변수를 선언하면 기본적으로 Stack 메모리 영역에 할당된다.
- Stack 영역 내부에 primitiveTest() 와 add()의 영역이 각각 나뉘어 있고, 서로 다른 변수가 존재한다.
- 그래서 num 값에 1을 더해도 v변수는 아무런 영향이 없다.
- 따라서 원시 타입의 전달은 값을 복사해서 전달하는 Call by Value 방식으로 동작한다는 것을 알 수 있다.
참조 타입(reference type) 전달 방식
- 참조 타입은 원시 타입과 조금 다르다.
- 변수는 Stack 영역에 생성되지만, 객체는 Heap 영역에 위치하며 Stack 영역에 있는 변수가 Heap 영역에 있는 개체를 바라보고 있는 형태다.
public class CallByExampleTest {
@Test
void referenceTest() {
int[] arr = { 10 }; // 주소 : 0x001
add(arr);
assertThat(arr[0]).isEqualTo(11);
}
void add(int[] arrArg) {
arrArg[0]++; // 주소 : 0x001
}
}
- 변수의 참조(주소) 값을 복사해서 전달해 referneceTest() 영역 add() 영역의 변수들은 모두 동일한 객체의 주소(0x001)를 바라보고 있다.
- add()에서 생긴 변수가 같은 주소 값을 참조하고 있기 때문에 값이 변경되었다.
public class main {
public static void main(String[] args) {
Sample sample = new Sample();
int var = 1; // primitive 타입 변수 int
int[] arr = { 1 }; // reference 타입 변수 int[] 배열
// 변수 자체를 보냄 (call by value)
add_value(var);
System.out.println(var); // 1 : 값 변화가 없음
// 배열 자체를 보냄 (call by reference)
add_reference(arr);
System.out.println(arr[0]); // 101 : 값이 변화함
}
static void add_value(int var_arg) {
var_arg += 100;
}
static void add_reference(int[] arr_arg) {
arr_arg[0] += 100;
}
}
- 똑같이 두 변수를 메서드의 입력값으로 보냈지만, 이렇게 결과의 차이가 일어나는 이가 바로 자체 값의 복사(Call by value)와 값의 주소 참조(call by reference) 가 일어 났기 때문이다.
1. main 스택 프레임에 두 변수가 담기게 된다.
- primitive 타입인 변수 var은 그대로 원시값 1을 지나게되며, reference 타입인 배열 변수 arr은 실제 데이터는 heap 영역에 저장되게 되고 이를 참조할 주소값을 저장하게 된다.
2. add_value() 에 입력값으로 변수 var을 넣어 호출한다.
- add_value() 메서드가 호출되면서, add_value 스택 프레임이 생성되고 그 안에 지역변수(매개변수) var_arg가 값을 1을 받은 채 생성되게 된다. ⇒ 그리고 잧체 메서드 로직으로 100을 더해 값은 101이 된다.
3. var 변수값은 변하지 않는다.
- add_value 스택 프레임 안에 있는 변수 var_arg가 바뀐 것이지, main 스택 프레임 안에 있는 변수 var 가 바뀐 것이 아니다.
- 매개변수 var_args는 그저 변수 var로 부터 원시값을 복사하여 받는 것 뿐이기 때문이다. (call by value)
- 즉, 메인에 정의되어 있는 var 변수와 add_value 메서드에 정의되어 있는 var_arg 변수는 서로 완전히 남남이다.
4. add_reference() 메서드에 입력값으로 변수 arr를 넣어 호출한다.
- add_reference() 메서드가 호출되면서, add_reference 스택 프레임이 생성되고 그 안에 지역변수(매개변수) arr_arg가 생성된다.
- 이때 전의 add_value() 메서드 호출 때 처럼 변수의 값이 복사되어 파라미터에 넘겨지는데, 자세히 살펴보니 스택 프레임에 있는 arr 변수가 들고 있는 값은 주소값이다.
- 그래서 메서드의 입력값으로 주소값이 복사되어 넘겨지게 된다.
- 따라서 결과적으로 두 변수 arr 와 arr_arg는 같은 주소값을 들고 있게 되는 것이고, 이 주소가 가리키는 메모리는 같기 때문에 두 변수는 하나의 데이터를 동시에 참조하고 있다고 말 할 수 있다.
- 마지막으로 arr_arg 변수가 가리키는 값 1을 불러와 100을 더하니 heap 영역에 있는 데이터는 101로 변경된다.
4. 마지막으로 변수 arr을 출력해보면 값이 변경됨을 알 수 있다.
출처
https://dev-coco.tistory.com/189
https://www.youtube.com/watch?v=-YLSKwZaCUM
'이론 > 자바' 카테고리의 다른 글
[Java] 동일성(==)과 동등성(equals) (0) | 2024.10.07 |
---|---|
[Java] MyBatis (0) | 2024.09.23 |
[Java] 클래스,객체, 인스턴스 차이 (0) | 2024.09.20 |
[Java] Scanner 와 BufferedReader (0) | 2024.09.20 |
[Java] 자바 메모리 구조, JVM (0) | 2024.09.18 |