두 방식은 메소드 (함수, Function)을 호출할 때 해당 메소드에 파라미터를 넘겨주는 방식이다.
이 중 값을 넘겨주는 방식을 Call by value, C++에서는 포인터라고 표현되는 참조값을 넘겨주는 방식을 Call by reference라고 한다.
Call by value는 단순히 값만 넘겨주는 방식이므로 파라미터로 넘겨준 값이 변해도 원래 변수의 값은 변하지 않는다.
하지만 Call by reference는 참조값을 넘겨주는 방식이기에 변수의 값이 변하면 원래 변수의 값도 변하게 된다.
그렇다면 JAVA는 Call by value 일까, 아니면 Call by reference일까?
아래의 예시를 보자.
public class HelloWorld{
public static class example {
private int index;
example(int index) {
this.index = index;
}
public int getIndex() { return index; }
public void setIndex(int index) { this.index = index; }
}
public static void swap(int num1, int num2) {
int tmp = num1;
num1 = num2;
num2 = num1;
}
public static void swap(example obj1, example obj2) {
example tmp = obj1;
obj1 = obj2;
obj2 = obj1;
}
public static void changeValue(example obj) {
obj.setIndex(999);
}
public static void main(String []args){
int num1 = 100;
int num2 = 200;
System.out.println("===Swap 전===");
System.out.println(num1 + " / " + num2); // 100 / 200
swap(num1, num2);
System.out.println("===Swap 후===");
System.out.println(num1 + " / " + num2); // 100 / 200
System.out.println("\n");
example obj1 = new example(100);
System.out.println("===변경 전===");
System.out.println(obj1.getIndex()); // 100
changeValue(obj1);
System.out.println("===변경 후===");
System.out.println(obj1.getIndex()); // 999
}
}
출력을 비교해보면 일반 자료형인 int는 값이 바뀌지 않았고, 객체형 자료형인 obj1의 값은 변경되었다.
이를 토대로 많은 사람들이 JAVA의 일반 자료형은 Call by Value 형식으로, 객체형 자료형은 Call by Reference 방식으로 전달된다고 설명한다.
하지만 아래의 예시를 보자.
public class HelloWorld{
public static class example {
private int index;
example(int index) {
this.index = index;
}
public int getIndex() { return index; }
public void setIndex(int index) { this.index = index; }
}
public static void swap(int num1, int num2) {
int tmp = num1;
num1 = num2;
num2 = num1;
}
public static void swap(example obj1, example obj2) {
example tmp = obj1;
obj1 = obj2;
obj2 = obj1;
}
public static void changeValue(example obj) {
obj.setIndex(999);
}
public static void main(String []args){
int num1 = 100;
int num2 = 200;
System.out.println("===Before Swap===");
System.out.println(num1 + " / " + num2); // 100 / 200
swap(num1, num2);
System.out.println("===After Swap===");
System.out.println(num1 + " / " + num2); // 100 / 200
System.out.println("\n");
example obj1 = new example(100);
System.out.println("===Before Change===");
System.out.println(obj1.getIndex()); // 100
changeValue(obj1);
System.out.println("===After Change===");
System.out.println(obj1.getIndex()); // 999
System.out.println("\n");
example obj2 = new example(100);
System.out.println("===Before Swap===");
System.out.println(obj1.getIndex() + " / " + obj2.getIndex()); // 999 / 100
swap(obj1, obj2);
System.out.println("===After Swap===");
System.out.println(obj1.getIndex() + " / " + obj2.getIndex()); // 999 / 100
System.out.println("\n");
}
}
obj 끼리 swap을 했는데 값이 유지되었다. Call by Reference 방식으로 전달된 것이 맞다면 있을 수 없는 현상이다.
즉, 결론부터 말하자면 Java는 Call by Value 형식이다.
그럼에도 불구하고 changeValue에서 원본 객체의 값이 변경된 이유는 어째서일까?
그 이유는 자바에서 파라미터로 전송하는 방식은 엄밀히 설명하면 참조값(주소)를 넘기는 것이 아니라, 그 참조값을 가리키는 새로운 참조값을, 즉 참조값을 복사하여 보내는 방식이기 때문이다.
그렇다면 위의 코드는 다음과 같이 돌아가게된다.
1) changeValue의 경우
- 복사한 주소를 보낸다. 그리고 해당 주소가 가리키는 객체의 값을 변경한다.
2) swap의 경우
- 복사한 주소를 보낸다. 그리고 두 개의 주소를 서로 변경한다.
changeValue의 경우 중요한 것은 해당 주소가 가리키는 객체의 값을 변경한다는 점이다. 이로 인해 원본 객체의 값도 당연히 변화하게 되고, Call by Reference와 같은 효과가 나타난다.
하지만 swap의 경우는 다르다. 두 개의 주소를 가지고 있는데, 그저 그 두 개의 주소가 가리키는 값들을 변경했다.
하지만 이 주소들은 원래의 주소가 아니라 주소를 복사한 값이기에 당연히 함수가 종료되면 사라지게 된다.
'언어 > JAVA' 카테고리의 다른 글
리눅스 OS에 명령어 보내기 (0) | 2022.07.21 |
---|---|
[JAVA] 온점 단위 Split (0) | 2022.03.10 |
[JAVA] == vs equals vs hashCode (0) | 2021.03.30 |
[JAVA] Garbage Collection (GC) (0) | 2021.03.30 |
[JAVA] String, StringBuffer, StringBuilder (0) | 2021.03.30 |