본문 바로가기
Language/Java

[Java] 함수 호출 'CallByValue' 와 'CallByReference' 의 차이

by 드럼치는 코린이 2021. 9. 3.
728x90
반응형

들어가면서..

Java를 사용하면서 클래스에 필요한 기능을 추가하기 위해 메서드를 만들어 애플리케이션을 구현한 경험은 누구나 있을 것으로 생각합니다. 하지만 저는 Java에 대해 공부하면서 메서드의 리턴 타입이 void인 매개변수가 없는 메서드 외에 매개변수가 존재하는 메서드의 경우 값을 복사하는지 or 직접 참조하는지에 따라 함수 호출 방법이 다르다는 것을 최근에야 알게 되었습니다.

바로 그 주인공인 CallByValue와 CallByReference가 오늘의 주제인데 차근차근 알아보도록 하겠습니다.


함수 호출 방법

함수 호출 방법에는 크게 2가지가 있습니다.

  • CallByValue
  • CallByReference

먼저 CallByValue는 말 그대로 '값에 의한 호출'을 말하고 CallByRefernce는 '참조에 의한 호출'을 말합니다.


처리 방법과 장단점

1. CallByValue (값의 의한 호출)

함수에 전달되는 매개변수를 실제 매개변수(인자)라고 부르고 함수가 전달받은 매개변수를 형식 매개변수(매개변수)라고 부릅니다.

CallByValue는 미리 선언해둔 모든 타입의 실제 매개변수의 값, 즉 원본 값을 형식 매개변수에 복사하여 처리합니다.
그리고 실제 매개변수와 형식 매개변수는 각각 서로 다른 메모리에 저장됩니다.

따라서 함수 내에서의 변경은 실제로 전달된 실제 매개변수에 반영되지 않습니다.

프로그래밍 구조상 CallByValue를 하면 복사(서로 다른 메모리에 저장)가 되기 때문에 메모리량이 늘어나는데,
요즘에는 기기의 성능이 좋아져서 상관이 없다지만 많은 계산이 들어간다면 과부하의 원인이 됩니다.
하지만 복사 처리가 되기 때문에 원래의 값은 영향을 받지 않아서 안전하다는 특징이 있습니다.

· 장점 : 값을 복사하여 처리하기 때문에 매개변수로 전달된 값이 보존이 되고 안전합니다.
· 단점 : 값을 복사하기 때문에 메모리 사용량이 늘어납니다.

 

2. CallByReference (참조의 의한 호출)

CallByReference는 실제 매개변수와 형식 매개변수 모두 동일한 메모리를 참조합니다.

따라서 함수 내에서 변경한 내용이 실제로 전달된 실제 매개변수에 반영됩니다.

· 장점 : 복사하지 않고 직접 참조를 하기 때문에 실행 속도가 빠릅니다.
· 단점 : 장점이자 단점인데 직접 참조를 하기 때문에 원래의 값이 영향을 받습니다.

 


예제

CallByValue와 CallByReference의 설명으로 자주 등장하는 swap 예제입니다.

[ CallByValue 예제 ]

public class CallByValue {
	
	public static void swap(int x, int y) {

		int temp = x;
	
		x = y;
	
		y = temp;

	}
	
	public static void main(String[] args) {
		
		int a = 10;

		int b = 20;

		System.out.println("swap() 호출 전 : a = " + a + ", b = " + b);

		swap(a, b);

		System.out.println("swap() 호출 후 : a = " + a + ", b = " + b);
	}

}


결과는 다음과 같습니다.

swap() 호출 전 : a = 10, b = 20
swap() 호출 후 : a = 10, b = 20


"어? 분명 swap() 메서드에 a, b를 넘겼으니까 값이 바뀌어야 하는 거 아냐?"

네, 아닙니다. 결론을 먼저 말씀드리자면 swap() 메서드 호출 시에 사용한 인자 a, b와 swap() 메서드 내의 매개변수 x, y는 서로 다르기 때문입니다.

아래 그림을 보시면 이해가 빠르게 되실 겁니다.

main()에서 선언된 변수 a와 b가 각각 메모리의 0x0001번지와 0x0005번지에 할당되었다고 가정합니다. 할당된 메모리 변수에는 각각 10과 20의 값이 저장됩니다. 이후, swap() 메서드 호출 시에 사용한 인자 a와 b는 할당된 메모리 주소가 아닌 메모리에 담겨 있던 값만이 복사되어 swap() 메서드 내부의 매개변수 x와 y의 메모리 주소에 담기게 됩니다. 당연하게도 swap() 메서드가 수행하는 동안 사용되는 변수들은 main()에 존재하는 a와 b가 아닌 swap() 내부에 새로 생성된 x와 y이기 때문에 메서드 수행 후에도 결과 값에 변화가 없습니다.

그럼 이어서, Call by reference에 대해 알아보겠습니다.

[ CallByReferecnce 예제 ]

Call by reference는 메서드 호출 시에 사용되는 인자가, 값이 아닌 주소(Address)를 넘겨줌으로써, 주소를 참조(Reference)하여 데이터를 변경할 수 있습니다.

public class CallByReference {
	
	int value;

	CallByReference(int value) {

		this.value = value;

	}

	public static void swap(CallByReference x, CallByReference y) {

		int temp = x.value;
	
		x.value = y.value;
	
		y.value = temp;

	}
	
	public static void main(String[] args) {
		
		CallByReference a = new CallByReference(10);

		CallByReference b = new CallByReference(20);

		System.out.println("swap() 호출 전 : a = " + a.value + ", b = " + b.value);

		swap(a, b);

		System.out.println("swap() 호출 전 : a = " + a.value + ", b = " + b.value);
	}

}


결과는 다음과 같습니다.

swap() 호출 전 : a = 10, b = 20
swap() 호출 전 : a = 20, b = 10


이번에는 원하는 대로 a와 b의 값이 바뀌어서 잘 나왔습니다.

그림을 보며 설명하도록 하겠습니다.

main()에서 선언된 CallByReference 타입의 변수 a와 b는 각각 객체를 생성하여 0x0001번지와 0x0005번지에 저장된 10과 20의 주소 값을 저장하게 됩니다. 이후, swap() 메서드 호출 시에 인자 a와 b는 메모리에 저장된 주소 값을 복사하여 매개변수 x와 y의 메모리에 저장합니다. 결국 swap() 메서드는 10과 20이 저장된 0x0001번지와 0x0005번지의 주소를 참조하여 연산하기 때문에, 연산 결과에 따라 원본 데이터가 변하게 됩니다. main()에서 선언된 변수 a와 b는 각각 0x0001번지와 0x0005번지를 가리키고 있기 때문에 변한 데이터를 불러들여 결과 값이 변하게 됩니다.


논점

Java는 현재 CallByValue냐 CallByReferecnce냐 논쟁이 있는데, 현재로서는 CallByValue로 인식되고 있다.

Java 의 경우 항상 call by value 입니다. 자바는 객체를 메서드로 넘길 때 참조하는 지역변수의 실제 주소를 넘기는 것이 아니라 그 지역변수가 가리키고 있는 힙 영역의 객체를 가리키는 새로운 지역변수를 생성하여 그것을 통하여 같은 객체를 가리키도록 하는 방식입니다.
call by value는 메모리의 저장값, call by reference 는 메모리의 주소값을 보내는데 java 의 경우 인자값을 받아서 해당값을 변경하더라도 호출했던 class 내에서의 객체가 변경되지는 않습니다.

더 자세한 내용은 해당 포스팅을 참조해보면 좋을 것 같습니다.


[ Reference ]

https://re-build.tistory.com/3

https://jaimemin.tistory.com/1479

728x90
반응형

댓글