[Java] 자바는 Call by Value (feat. C++)
zl존석동
·2022. 1. 4. 11:29
자바가 왜 Call By Value인지 공부해보자
Call By ???
프로그래밍 언어의 함수의 파라미터 호출 방법을 말한다.
Call By Reference
'참조에 의한 호출' 을 말하며 메소드 동작을 위해 전달 받은 파라미터의 주소를 참조해 직접적으로 값에 영향을 주는 것을 말한다.
Call By Value
'값에 의한 호출' 을 말하며 메소드로 파라미터를 넘길 때 그 값을 복사하여 메소드 내부에서 사용하는 형태를 말한다.
자바는 Call By Value 이다.
public static void main(String[] args) {
int num = 10;
plus(num);
int result = num;
System.out.println(result);
}
static void plus(int input) {
input = input + 25;
}
다음 코드의 출력 결과는 10이다.
메인 메소드에서 plus 메소드를 사용할 때 num 이라는 파라미터는 단지 num 이라는 변수가 가진 10이라는 값을 전달해줄 뿐이다.
그냥 plus() 메소드의 input 이라는 파라미터는 num 이 가진 10이라는 값이 할당된, 메소드 내부에서 사용하기 위해 있는 로컬 변수라고 생각하면 될 것 같다.
input 이라는 파라미터는 결국 값만 num에게서 받은, num 과는 상관 없는 로컬 변수이며 메소드 내부에서 무엇을 하던 메소드 동작이 종료되면 소멸되게 된다.
리턴을 통해 외부 스코프로 넘겨줘 다른 변수에 할당한다면 내부에서의 변화가 의미있었던 것으로 사용될 수 있겠지만
위와 같이 리턴이 없다면 의미없는 동작만 하고 소멸되버리는 것이다.
#include <iostream>
using namespace std;
void pluss(int& input){
input += 25;
};
int main()
{
int num = 10;
pluss(num);
cout << num;
return 0;
}
C++ 처럼 레퍼런스 변수를 활용하면 Call By Reference 를 할 수 있다.
pluss 메소드에서 num 의 주소를 직접 참조해와서 직접 값을 변경할 수 있게 되는 것이다.
자바도 이게 되는데 Call By Reference 아닌가요?
객체를 파라미터로 받아 메소드에서 객체의 값을 변경할 수 있는데요?
public class Cbv {
public static void main(String[] args) {
A a = new A("ABC");
int aCode = a.hashCode();
run(a);
System.out.println(a.name); // KKK 출력
}
static void run(A aInput) {
aInput.name = "KKK";
int aInputCode = aInput.hashCode();
}
}
class A {
String name;
public A(String name) {
this.name = name;
}
}
위의 코드는 KKK 라는 결과를 출력하는데 디버깅을 해보며 왜 Call By Value인지 알아봅시다.
먼저 메인 메소드에서 run(a) 를 하기 직전까지의 상태를 확인해보면 다음과 같다.

A 객체인 a 는 31 이라는 아이디를 가지고 있다.
이 a 값을 파라미터로 하는 run(a) 를 실행하고 메소드 종료 전의 상태를 확인해봅시다.

파라미터인 aInput과 a 가 같은 id를 가지고 있음을 알 수 있다.
name의 경우 앞서 "ABC" 일 때와 값도 id도 다른 것을 확인할 수 있다.
a 가 가지고 있는 A 객체에 대한 주소값을 run() 메소드의 파라미터로 복사하여 메소드에서 사용하게 되기 때문에
각각 다른 스코프에 존재하는 두 변수의 주소값이 같은 것이고
같은 주소가 가리키는 곳에 존재하는 name 이라는 속성의 변경이 유효하게 된 것이다.
딱히 전달 받은 파라미터의 주소를 참조해서 직접 값을 변경하는게 아니라 가진 값 자체가 그냥 주소였다고 할 수 있는 것이다.

아직 잘 모르겠다. 다음과 같은 코드를 한번 봅시다.
public class Cbv2 {
public static void main(String[] args) {
// 과연 call by ref일까??
AA aa = new AA("김갑환");
run(aa);
// 결과는??
System.out.println(aa.name);
}
static void run(AA inputAA) {
AA myAa = new AA("최번개");
inputAA = myAa;
}
}
class AA {
String name;
public AA(String name) {
this.name = name;
}
}
Call By Reference 라면 파라미터의 주소를 참조해 값을 변경하기 때문에
run() 메소드에서의 inputAA = myAa 의 경우 myAa 의 값 자체가 inputAA 의 주소가 가진 값에 할당이 되어야 할 것이다.
하지만 이 코드의 출력결과는 최번개가 아닌 김갑환을 보여줄 것이다.
이전 코드 예시에서 단지 같은 주소값을 가져와 그 주소가 가리키고 있는 곳 내부의 속성을 변경했기 때문에 마치 주소를 이용해 값을 변경하는 것처럼 보일 수 있지만 결국은 변수가 가진 객체에 대한 참조값 그 자체를 복사해 이용하는 것 뿐이었다는 것이다.
위의 코드에서 run() 메소드는 AA aa 의 주소값을 inputAA 라는 파라미터로 받아온 다음 그 파라미터의 값에 그냥 새로운 myAa 라는 객체의 주소값을 할당해보고 메소드를 종료하는 것 뿐이다.
C++ 로 구현한 다음 코드를 보면 진짜 Call By Reference 가 무엇인지를 확인해볼 수 있다.
#include <iostream>
using namespace std;
class AA {
public:
string name;
AA(string name) {
this->name = name;
}
};
void run(AA *inputAA){
AA myAA = AA("최번개");
*inputAA = myAA;
}
int main()
{
AA aa = AA("김갑환");
run(&aa);
cout << aa.name;
return 0;
}
이 코드는 최변개를 출력하게 된다.
run() 메소드에서 자바는 파라미터 자체가 복사해와서 가지고 있는 (주소)값을 새로운 myAA(주소)로 할당해버리는 것과 다르게
C++ 에서는 포인터를 이용하여 전달받은 파라미터의 주소 자체에 있는 값을 변경하는 것이다.
복사되거나 한 것이 아니라 처음 생성한 AA("김갑환") 객체 자체의 주소를 활용하였기 때문에
변경이 유효하게 된다.
'Java' 카테고리의 다른 글
| [Java] Abstract , Interface (0) | 2022.01.09 |
|---|---|
| [Java] 람다식이란 (0) | 2022.01.06 |
| [Java] 중첩 클래스 (0) | 2022.01.05 |
| [Java] 자바 메모리 구조와 변수 타입 (0) | 2021.12.22 |
| [Java] Java 의 동작 (0) | 2021.12.20 |