자바는 Call-By-Value일까, Call-By-Reference일까?
결론부터 말하자면 자바는 Call-By-Value이다.
우리가 흔히 객체를 생성하고 객체를 수정하면 객체 원본의 값도
같이 수정되기 때문에 Call-By-Reference라고 생각하기 쉽다.
예를 위해 클래스를 하나 생성해보겠다.
public class CallByTest {
private int value;
public void setValue(int value) {
this.value = value;
}
public int getValue() {
return value;
}
public static void run(CallByTest a) {
a.value = 100;
}
public static void main(String[] args) {
CallByTest a = new CallByTest();
a.setValue(10);
System.out.println(a.getValue());
run(a);
System.out.println(a.getValue());
}
}
---------------------------------------
10
100
이 코드는 a라는 객체를 생성하고 10이라는 값을 담고 있다.
그 후 run 메소드를 통해 a에 100을 대입하고 메소드가 끝난 후 다시 값을 찍어보니
변경된 값이 나왔다.
이 부분에서 우리가 흔히 Call-By-Reference라고 착각을 한다.
하지만 run 메소드로 매개변수를 넘기는 부분에서 직접적인 참조값을 넘기는 것이 아닌
주소가 가지고 있는 값을 복사해서 넘기기 때문에 이는 Call-By-Value이다.
복사된 주소 값으로 참조를 하고 있어 객체의 내용이 변경되는 것이다.
이 부분에서는 아직 잘 이해가 안될 수 있다.
하지만 이러한 클래스의 경우는 어떻게 될까?
public class CallByTest {
private int value;
public void setValue(int value) {
this.value = value;
}
public int getValue() {
return value;
}
public static void run(CallByTest a, CallByTest b) {
a.value = 100;
b = a;
System.out.println(a.getValue() + ", " + b.getValue());
}
public static void main(String[] args) {
CallByTest a = new CallByTest();
CallByTest b = new CallByTest();
a.setValue(10);
b.setValue(20);
System.out.println(a.getValue() + ", " + b.getValue());
run(a, b);
System.out.println(a.getValue() + ", " + b.getValue());
}
}
------------------------------------------
10, 20
100, 100
100, 20
a와 b라는 객체를 생성 후 10, 20이라는 값을 넣었다.
그 후 run 메소드를 통해서 a에 100을 대입하고 b에 a를 대입했다.
값을 찍어보니 run 메소드 내에서는 100, 100으로 대입된 값이 찍힌다.
main에서 다시 찍어봤다.
자바가 Call-By-Reference라면 100, 100이 나왔어야할 것이다.
하지만 main에서 다시 찍어보니 b의 값이 변경되지 않고 다시 20이 되었다.
이 부분에서 우리는 자바가 Call-By-Reference가 아닌 Call-By-Value라는 것을 알 수 있다.
자세히 알기 위해서는 메모리 영역을 알아야 하는데,
Stack area와 Heap area가 어떻게 되는지 알아야 한다.
현재 new 객체 생성 후 setValue로 값을 삽입한 부분까지의 메모리 내부 상황이다.
CallByTest a로 변수를 선언했을 때 Stack 영역에 a가 쌓이게 된다.
그 후 new 연산자를 사용했을 경우 a의 주소를 참조하는 Heap 영역에 메모리가 할당된다.
run 메소드가 실행되고 나서가 중요한데, run 메소드가 실행되면
main이 가지고 있던 a,b의 주소 값을 복사하여 run의 a,b가 독자적으로 가지게 된다.
stack area의 run이 가지고 있는 주소 값을 run 메소드의 매개변수로 전달한다.
주소 값을 참조하는 것이 아닌 복사하여 가져가는 Call-By-Value의 방식이다.
여기서 a는 a.value를 통해 값을 변경 시 a가 가지고 있는 주소 값을 통해
객체의 값을 변경하게 된다.(main a와 run의 a가 같은 주소를 가리키고 있음)
b = a가 실행되고 난 뒤 run 함수 내에서는 현재 a와 b가 같은 주소 값을 가지고 있다.
여기서 가장 중요한 점은 현재 a, b가 같은 주소 값을 가지고 있는 영역이 Stack area 영역의
run의 주소 값이라는 것이다.(참조한 값을 변경한 것이 아닌 단순히 a의 주소 값을 복사)
그렇기때문에 실제 Heap area의 주소 값은 바뀌지 않는다.
run 메소드 내에서 a와 b의 주소를 찍어보면 같은 주소를 가리키고 있기는 하다.
(단순히 a의 주소 값을 b에 대입했기 때문)
run 메소드가 종료되면 Stack 영역에 할당되어 있던 run의 a와 b는 사라지게 된다.
그렇기 때문에 main에서 a와 b의 value를 찍어보면 100, 20이 출력되는 것이다.
메모리 내부 구조에서 이렇게 동작이 되기 때문에 자바는 Call-By-Value이다.
'JAVA' 카테고리의 다른 글
[JAVA] JAVA의 역사와 특징 (0) | 2022.09.20 |
---|---|
[JAVA] JAVA 컴파일 과정과 JVM (0) | 2022.02.27 |
[JAVA] JAVA는 인터프리터? 컴파일? (0) | 2022.02.27 |
[JAVA] 자바는 무엇인가?(자바의 특징) (0) | 2022.02.27 |
[JAVA] for문, 향상된 for문, for Each문 (0) | 2022.01.13 |