Call by Value와 Call by Reference

한눈에 보기

자바는 기본형이든 참조형이든 언제나 값에 의한 호출만 사용하며, 참조형은 객체가 아니라 참조값이 복사되어 전달된다. swap 실패 예제와 스택 프레임 관점으로 그 근거를 설명한다.

최종 업데이트 2026.09.17

값에 의한 호출(Call by Value)은 메서드를 호출할 때 인자의 값을 복사해서 전달하는 방식이고, 참조에 의한 호출(Call by Reference)은 변수 자체에 대한 별칭을 전달해 호출자 쪽 변수까지 바꿀 수 있는 방식이다. 자바는 기본형이든 참조형이든 언제나 값에 의한 호출만 사용하며, 참조형의 경우 객체가 아니라 객체를 가리키는 참조값이 복사되어 전달된다. 이 구분을 정확히 이해해야 메서드 안에서 매개변수를 조작했을 때 어떤 변화가 호출자에게 보이는지 예측할 수 있다.

두 방식의 개념

값에 의한 호출에서는 호출 시점에 인자의 현재 값이 매개변수로 복사된다. 이후 메서드 안에서 매개변수를 아무리 바꿔도 원본 변수는 영향을 받지 않는다. 반면 참조에 의한 호출에서는 매개변수가 호출자 변수의 별칭(alias)이 되므로, 매개변수에 새 값을 대입하면 호출자의 변수도 함께 바뀐다. C++의 참조 매개변수나 C#의 ref 키워드가 진짜 참조 호출의 예다. 자바에는 이런 문법이 존재하지 않는다.

자바는 왜 항상 Call by Value인가

자바에서 참조형 변수에 저장된 것은 객체 그 자체가 아니라 힙(heap)에 있는 객체의 위치를 가리키는 참조값이다. 메서드를 호출하면 이 참조값이 복사되어 새 스택 프레임의 지역 변수 배열에 들어간다. 즉 호출자와 피호출자는 같은 객체를 가리키는 서로 다른 두 개의 참조를 갖는다.

그래서 두 가지 결과가 갈린다. 복사된 참조를 통해 객체의 내부 상태를 바꾸면 같은 객체를 보고 있는 호출자에게도 변화가 보인다. 그러나 매개변수에 새 객체를 대입하는 것은 복사본을 다른 곳으로 돌려 놓는 일일 뿐이라 호출자의 참조에는 아무 영향이 없다. 이 두 번째 성질 때문에 자바를 참조 호출이라고 부르면 틀린 설명이 된다.

코드로 확인하기

class Box {
    int value;
    Box(int value) { this.value = value; }
}

public class PassingDemo {
    static void reassign(Box b) {
        b = new Box(999);      // 복사된 참조만 새 객체로 바뀐다
    }

    static void mutate(Box b) {
        b.value = 999;         // 같은 객체의 상태를 바꾼다
    }

    static void swap(Box x, Box y) {
        Box tmp = x;           // 복사본끼리만 교환된다
        x = y;
        y = tmp;
    }

    public static void main(String[] args) {
        Box box = new Box(1);
        reassign(box);
        System.out.println(box.value); // 1, 원본 참조는 그대로다

        mutate(box);
        System.out.println(box.value); // 999, 객체 상태 변경은 보인다

        Box a = new Box(1), b = new Box(2);
        swap(a, b);
        System.out.println(a.value + " " + b.value); // 1 2, 교환 실패
    }
}

swap 메서드가 실패하는 것이 자바가 값 호출이라는 결정적 증거다. 진짜 참조 호출 언어라면 호출자 쪽 a와 b가 서로 바뀌었을 것이다. 자바에서 두 변수를 교환하려면 배열이나 컨테이너 객체에 담아 그 내부를 바꾸는 우회가 필요하다.

JVM 스택 프레임 관점

메서드가 호출되면 자바 가상 머신(JVM)은 호출 스레드의 스택에 새 프레임을 쌓고, 인자 값들을 피연산자 스택에서 꺼내 새 프레임의 지역 변수 배열에 복사한다. 기본형이면 값 자체가, 참조형이면 참조값이 복사된다는 점만 다를 뿐 복사라는 사실은 같다. 메서드가 끝나면 프레임이 통째로 사라지므로 매개변수에 가한 대입은 흔적도 남지 않는다. 반면 힙의 객체는 프레임과 독립적으로 살아 있으므로, 참조를 따라 들어가 바꾼 필드 값은 호출이 끝나도 유지된다.

혼동하기 쉬운 지점 정리

메서드 안의 동작호출자에게 보이는가이유
기본형 매개변수에 새 값 대입보이지 않음값의 복사본만 변경
참조형 매개변수에 새 객체 대입보이지 않음참조의 복사본만 변경
매개변수가 가리키는 객체의 필드 수정보임힙의 동일 객체를 공유
매개변수로 받은 컬렉션에 원소 추가보임힙의 동일 컬렉션을 공유

String이나 Integer 같은 불변(immutable) 객체를 전달하면 애초에 내부 상태를 바꿀 방법이 없으므로 메서드가 호출자에게 영향을 줄 수 없다. 이런 성질은 방어적 프로그래밍에 유리하며, 공유 가능한 가변 객체를 넘길 때는 필요에 따라 방어적 복사(defensive copy)를 고려하는 것이 좋다.

면접에서 이렇게 물어본다

  • 자바는 Call by Value인가 Call by Reference인가, 그렇게 판단한 근거를 코드로 설명해 보라.
  • 메서드 안에서 매개변수 객체의 필드를 바꾸면 호출자에게 보이는데, 새 객체를 대입하면 보이지 않는 이유는 무엇인가.
  • 자바에서 swap 메서드를 일반적인 방법으로 구현할 수 없는 이유를 설명해 보라.
  • 메서드 호출 시 인자가 JVM 스택 프레임에 어떻게 전달되는지 설명해 보라.
  • 불변 객체를 인자로 전달하는 것이 안전하다고 말하는 이유는 무엇인가.
Java 문서 전체 보기

이 개념, 내 코드에서는 어떻게 물어볼까요

데브핏(DevFit)에 채용공고 URL을 붙여 넣고 레포지토리를 연결하면, 내가 쓴 커밋에서 예상 질문과 어필 포인트를 만듭니다. 하루 10회까지 무료입니다. 합불 판정이나 지원자 랭킹은 만들지 않고, 최종 판단은 사람이 합니다.

공고 넣고 시작하기