자바에는 Call by reference 가 없습니다. 이에 대해서 이유를 살펴보겠습니다.
🐻 Call by value, Call by reference
프로그래밍을 하다 보면 꼭 알고 넘어가야 하는 개념이 있습니다. 바로 Call by Value, Reference입니다. 어떤 언어를 공부하든 나오는 개념이기도 합니다.
함수의 매개변수에서 값을 복사하느냐 주소값을 참조하느냐에 따라 반환 결과가 달라집니다.
🐻 Call by value
Call by value란, 값을 호출하는 것을 의미합니다. 또는 값에 의한 호출, 값만 전달하는 방식이라고 합니다.
전달받은 전달받은 값을 복사하여 처리합니다. 전달받은 값을 변경해도 원본에 영향이 가지 않습니다. 두 변수의 값을 변경하는 예제를 통해서 살펴보겠습니다.
public class CallByValue {
public static void main(String[] args) {
int a = 10;
int b = 20;
swap(a,b);
System.out.println("a value = " + a + ", b value = " + b);
}
public static void swap(int x, int y) {
int temp = x;
x = y;
y = temp;
}
}
swap 메서드를 통해서 변수 a와 b의 값을 교체하는 메서드를 만들었습니다. main 스레드가 모든 코드를 실행하고 종료하게 되면 두 변수의 값이 swap 되기를 기대합니다. 하지만, 실제 출력을 보면 변경되지 않습니다.
스택 프레임
하나의 메서드에 필요한 메모리 덩어리를 묶어서 스택 프레임이라고 합니다.
하나의 메서드 당 하나의 스택 프레임이 필요하며, 메서드를 호출하기 직전 스택 프레임을 자바 Stack에 생성한 후 메서드를 호출하게 됩니다.
Call by value는 값만 전달합니다. 복사, 복제하여 메서드의 매개 변수를 넘길 때에는 원래 값은 놔두고, 전달되는 값이 진짜인 것처럼 보이게 합니다. 그래서 매개 변수를 받은 swap 메서드에서 값을 변경해도 원래 값은 변하지 않습니다. Stack에 새로운 스택 프레임이 생기면서 똑같은 값을 복사하여 전달하기 때문입니다.
- main 스택 프레임에 두 변수 a, b가 담깁니다.
- swap 메서드 파라미터에 값을 복사해서 넘깁니다.
- main 스택에 존재하는 변수 a와 b는 분리된 swap스택 프레임이 생깁니다.
- swap 스택 프레임에 복사된 값 10과 20이 x와 y에 복사됩니다.
- x와 y의 값을 변경합니다.
- swap 메서드가 끝나면 스택 프레임을 정리합니다. x, y의 변수를 정리합니다.
- main 스택 프레임으로 돌아옵니다. 분리된 a, b는 값 변경에 영향을 받지 않습니다.
🐻 Call by reference
참조에 의한 호출을 의미합니다. 전달받은 참조 자료형을 전달합니다.
public class CallByReference {
public static void main(String[] args) {
int number = 1;
int[] arr = {1};
addValue(number);
System.out.println(number); // 1: 값 변화가 없음
addReference(arr);
System.out.println(arr[0]); // 101: 값이 변경됨
}
private static void addValue(int number) {
number += 100;
}
private static void addReference(int[] arr) {
arr[0] += 100;
}
}
참조형은 stack 프레임이 생길 때 변수 값에 객체의 주소 값, 즉 Heap 영역에 저장된 주소 값이 복사되어 동일한 곳을 바라보고 있게 됩니다.
결과를 살펴보면 addReference 스택 프레임이 참조하고 있는 Heap에 저장된 객체의 값을 변경하며 main 스택 프레임에서도 영향을 받게 되었습니다.
이와 같이 참조 자료형도 호출된 메서드의 파라미터로 객체를 받으면 Stack에 새로운 스택 프레임이 만들어지면서 주소값 0x001의 값이 복사되어 넘어가게 됩니다.
🐻 자바에는 Call by Reference 개념이 없다.
지금까지 보면 call by value / call by reference의 차이가 나타나 자바가 Call by reference가 아니다는 것이 헷갈리게 됩니다. 글 초반에서 말했듯 자바 프로그래밍에서는 call by reference라는 것이 존재하지 않습니다.
왜냐하면 Java는 C언어와 달리 포인터(주소 값)를 철저하게 숨겨서 개발자가 직접 메모리 주소에 접근하지 못하게 했기 때문입니다.
결론적으로 자바에서의 파라미터는 call by value로서만 동작하며, 원시값이 복사되느냐 주소값이 복사되느냐 차이가 있을 뿐입니다.
🐻 C 언어의 call by reference
C++ 언어에서는 레퍼런스 혹은 참조자 (&)라는 개념이 따로 있으며, call by reference의 의미를 정확하게 이해할 수 있습니다. C 언에에서는 포인터 (*) 변수를 파라미터로 받게 하고 함수를 호출할 때 주소 연산자(&) 를 이용해 주소값을 직접 넘겨 직접적인 메모리 참조가 가능합니다.
#include <stido.h>
void swap(int *, int *);
int mai() [
int a = 10;
int b = 20;
swap(&a, &b);
}
void swap(int *a, int *b) {
int temp;
temp = *a;
*a = *b;
*b = temp;
}
코드를 살펴보면 swap 메서드를 호출할 때 a, b의 주소 값을 넘겨주고 있습니다. 이 과정을 하나씩 분리해서 살펴보겠습니다.
🐻 최초 실행 시 메모리 할당합니다.
🐻 swap 함수를 호출합니다.
swap 함수의 파라미터로 넘어온 주소 값이 포인터 변수 a, b에 대입됩니다.
🐻 swap 함수를 실행합니다.
참조 : C 언어 Call-by-Value & Call-By-Reference
C 언어에서는 이처럼 주소 값을 주소 연산자 (&)를 통해서 파라미터로 넘길 수 있었습니다. 하지만 자바에서는 포인터가 철저하게 숨겨져 주소 값을 개발자가 직접 핸들링할 수 없습니다.
🐻 정리 - 자바는 Call by value / Call by address
정리하면 Java는 Call by value로 동작하면서 원시값이 복사되느냐, 주소 값이 복사되느냐의 차이가 있을 뿐입니다. 이 때문에 자바는 오로지 call by value로서 동작합니다. 추가로 원시값을 복사하느냐, 주소값을 복사하느냐에 따라 반환 결과가 달라지기 때문에 이 둘을 구분하기 위해 call by value / call by address로 구분하기도 합니다.