
자바 개발자 사이에서 가장 흔하면서도 깊이 있게 다뤄지는 논쟁 중 하나는 바로 "자바의 파라미터 전달 방식"입니다. 면접 질문으로도 단골인 이 주제에 대해 많은 이들이 "기본형은 Call by Value이고, 참조형(객체)은 Call by Reference이다"라고 오해하곤 합니다.
결론부터 명확히 말씀드리면, 자바는 100% 'Call by Value' 방식으로 동작합니다. 이 글에서는 왜 자바가 참조형 변수를 다룰 때조차 Call by Value인지, 그리고 JVM 메모리 구조상에서 어떤 일이 벌어지는지 심도 있게 파헤쳐 보겠습니다.
1. 용어의 명확한 정의
논의를 시작하기 전, 두 용어의 핵심 차이를 이해해야 합니다.
- Call by Value (값에 의한 호출): 함수 호출 시 전달되는 변수의 '값'을 복사하여 전달합니다. 호출된 함수 내에서 값을 변경해도 호출자의 원래 변수에는 영향이 없습니다.
- Call by Reference (참조에 의한 호출): 함수 호출 시 변수의 '주소(참조)' 자체를 전달합니다. 함수 내에서 매개변수를 조작하면 호출자의 원래 변수가 가리키는 대상이 바뀌거나 변수 자체가 바뀔 수 있습니다.
2. 왜 자바는 100% Call by Value인가?
자바에서 변수가 저장하는 것은 무엇인지 생각해보면 답이 나옵니다. 자바의 변수는 크게 두 가지 타입으로 나뉩니다.
A. 기본형 (Primitive Type)
int, double, boolean 등은 변수 안에 실제 데이터 값을 직접 저장합니다. 함수에 전달할 때 이 값이 복사되므로 당연히 Call by Value입니다.
B. 참조형 (Reference Type)
Object, Array 등은 변수 안에 객체의 실제 메모리 주소(Reference)를 '값'으로 가집니다. 여기서 혼동이 발생합니다. 함수에 객체를 전달할 때 "주소값"이 복사되어 전달됩니다. 주소를 복사해서 전달하는 것 역시 결국 그 주소라는 '값'을 복사하는 것이기에 Call by Value에 해당합니다.
3. 결정적인 차이: "주소값을 바꿀 수 있는가?"
자바가 Call by Reference가 아님을 증명하는 가장 좋은 방법은 함수 내부에서 객체 자체를 새로 할당해 보는 것입니다.
public void swap(Person a, Person b) {
Person temp = a;
a = b;
b = temp;
// 이 시점에서 a와 b는 서로 바뀌었지만,
// 함수를 호출한 쪽의 원본 변수들은 아무런 변화가 없습니다.
}
만약 자바가 Call by Reference였다면, 위 swap 메서드 실행 후 외부의 객체 참조도 서로 바뀌었어야 합니다. 하지만 자바는 주소값을 복사해서 보낸 것뿐이기에, 메서드 내부의 a와 b라는 로컬 변수가 가리키는 방향만 바뀔 뿐 외부 변수에는 영향을 주지 못합니다.
4. 비교 요약표
자바의 전달 방식을 상황별로 정리하면 다음과 같습니다.
| 대상 타입 | 전달되는 내용 | 전달 방식 (공식) | 함수 내 수정 시 원본 영향 |
|---|---|---|---|
| 기본형 (Primitive) | 실제 데이터 값 복사 | Call by Value | 영향 없음 |
| 참조형 (Reference) | 객체의 주소값 복사 | Call by Value | 객체 내부 속성은 변경 가능 / 변수 자체는 변경 불가 |
5. 개발 시 주의할 점: 가변 객체(Mutable)와 불변 객체(Immutable)
Call by Value임에도 불구하고 list.add()나 person.setName() 같은 코드가 원본 객체에 영향을 주는 이유는, 복사된 주소값이 가리키는 힙(Heap) 영역의 실제 객체는 동일하기 때문입니다. 반면 String이나 Integer 같은 불변 객체는 함수 내에서 값을 변경하려고 하면 새로운 객체를 생성하여 주소값을 덮어쓰기 때문에, 마치 기본형처럼 원본에 영향이 없는 것처럼 보입니다. 이는 전달 방식의 차이가 아니라 객체의 불변성(Immutability) 때문임을 명확히 구분해야 합니다.
6. 결론
자바는 설계 철학상 포인터 연산을 직접 허용하지 않으며, 보안과 안정성을 위해 오직 Call by Value만을 사용합니다. 참조형 변수를 넘길 때 "참조(주소)를 넘긴다"는 표현보다는 "참조값(주소값)을 복사해서 넘긴다"라고 이해하는 것이 가장 정확합니다.
이 개념을 정확히 숙지한다면 예상치 못한 객체 값의 변동이나 사이드 이펙트(Side Effect)를 방지하는 견고한 코드를 작성할 수 있을 것입니다.
※ 내용 출처
- The Java Language Specification, Java SE 8 Edition - Section 4.3.1 (Objects)
- "Effective Java" by Joshua Bloch (3rd Edition)
- Oracle Java Documentation: Passing Information to a Method or a Constructor
'Language > Java' 카테고리의 다른 글
| [JAVA] Java 접근 제어자(Access Modifier)의 완벽 이해와 캡슐화 전략 (0) | 2026.01.15 |
|---|---|
| [JAVA] Java 패키지(Package)의 핵심 역할: 이름 공간 관리부터 모듈화의 초석까지 (0) | 2026.01.15 |
| [JAVA] 상수 선언 시 static final을 사용하는 기술적 배경과 메모리 효율성 (0) | 2026.01.15 |
| [JAVA] Java의 final 키워드 완벽 정리 : 클래스, 메서드, 변수별 차이점 (0) | 2026.01.14 |
| [JAVA] Java 변수의 스코프(Scope)와 생명주기 : 효율적 메모리 관리의 핵심 (0) | 2026.01.14 |