Java로 보는 Call by Reference / Call by Value

최혜미·2024년 2월 22일
0

Java

목록 보기
4/5
post-thumbnail

프로그래밍 언어에서 함수 호출 방식은 크게 Call by Reference(참조에 의한 호출)Call by Value(값에 의한 호출)로 나눌 수 있다.
이 두 방식은 함수 내에서 인자의 처리 방식에 큰 차이를 가져오며, 성능과 데이터 변경에 관한 동작에 영향을 미친다.
Java는 이 중 오직 Call by Value만을 사용한다는 점에서 다른 언어들과 차별점을 가지고 있다.
이를 구체적으로 살펴보자.


1. Call by Value (값에 의한 호출)

Call by Value는 메서드를 호출할 때, 실제 값을 복사하여 전달하는 방식이다.
즉, 메서드의 파라미터는 호출한 곳에서 넘겨받은 값의 복사본이므로, 함수 내에서 파라미터의 값을 변경하더라도 원래 변수에는 아무런 영향을 미치지 않는다.

📌 동작 원리:

  1. 메서드 호출 시, 호출자의 변수 값을 그대로 복사하여 수신자의 파라미터로 전달.
  2. 수신자의 파라미터가 복사된 값을 사용하므로, 메서드 내에서 수신자의 파라미터를 수정하더라도 호출자의 변수는 영향을 받지 않음.
public class Main {
    public static void modify(int value) {
        value = 10; // 수신자의 파라미터 값 변경
    }

    public static void main(String[] args) {
        int number = 5;
        modify(number);
        System.out.println(number); // 출력: 5, 원본 값에는 영향 없음
    }
}

위 예시에서 메서드 modify는 파라미터로 값을 전달받지만, 이 값은 복사된 값이기 때문에 number라는 원래 변수는 수정되지 않는다. Java에서 모든 기본형(Primitive Type) 변수는 이렇게 Call by Value 방식으로 동작한다.


2. Call by Reference (참조에 의한 호출)

Call by Reference는 메서드를 호출할 때 변수의 주소값(참조값) 을 전달하는 방식이다.
이 방식에서는 메서드가 호출자의 원본 변수와 같은 메모리 주소를 참조하므로, 함수 내에서 파라미터를 수정하면 원본 변수에도 변경 사항이 반영된다.

📌 주요 특징:

  1. 메모리 절약: 큰 데이터를 직접 복사하지 않고 참조만 전달하므로 메모리 사용이 줄어듦.
  2. 데이터 수정 가능성: 호출된 함수에서 수신자의 파라미터를 변경하면 원본 변수도 수정됨.

예시 (C++과 같은 Call by Reference를 지원하는 언어에서):

void modify(int &value) {
    value = 10; // 원본 변수가 직접 수정됨
}

int main() {
    int number = 5;
    modify(number);
    std::cout << number; // 출력: 10, 원본 값이 변경됨
    return 0;
}

이러한 방식은 대량의 데이터를 처리하거나 객체 상태를 변경해야 할 때 유용하지만, 변수의 상태를 예측하기 어렵게 만들 수 있어 주의가 필요하다.


3. Java에서의 동작: ⭐️오직 Call by Value⭐️

Java는 항상 Call by Value로만 동작한다.
하지만 여기서 혼동할 수 있는 부분은 참조 타입(Reference Type) 객체의 전달 방식이다.
원시 타입(Primitive Type)은 값이 그대로 복사되지만, 참조 타입은 객체의 참조 주소가 복사된다.

📌 동작 방식:

  • 원시 타입(Primitive Type): Stack 영역에 값 자체가 저장되며, 이를 복사하여 전달.
  • 참조 타입(Reference Type): 객체 자체는 Heap 영역에 저장되고, Stack 영역의 변수는 객체의 주소값을 저장. 메서드 호출 시 이 주소값이 복사되기 때문에, 수신자의 파라미터는 같은 객체를 가리키지만, 참조 변수 자체는 서로 다른 변수로 존재.

❗️즉, 참조 타입의 객체를 메서드로 전달해도 그 주소값만 복사되기 때문에, 원본 참조 변수를 바꾸지 않는 한 객체 내부 데이터는 수정이 가능하다.
하지만 참조 자체를 변경해도 원본 객체의 참조에는 영향이 없다

public class Main {
    static class MyObject {
        int value = 5;
    }

    public static void modify(MyObject obj) {
        obj.value = 10; // 객체의 필드 값 변경
        obj = new MyObject(); // 새로운 객체로 참조 변경
        obj.value = 20; // 새로운 객체의 필드 값 변경
    }

    public static void main(String[] args) {
        MyObject obj = new MyObject();
        modify(obj);
        System.out.println(obj.value); // 출력: 10, 원본 객체의 필드 값은 변경됨
    }
}

4. Call by Value의 장점과 단점

📌 장점:

  1. 안정성: 원본 데이터가 보호되므로, 함수 호출 중 예기치 않은 데이터 수정으로 인한 버그를 방지할 수 있다.
  2. 예측 가능성: 함수 내에서 값을 수정하더라도, 호출자의 데이터는 변하지 않으므로 코드의 흐름을 추적하기 쉽다.

📌 단점:

  1. 성능 문제: 대량의 데이터를 함수로 전달할 때, 값을 복사하는 방식은 메모리와 시간 소모가 크다.
  2. 객체 수정의 한계: 참조 타입 객체는 내부 데이터를 수정할 수 있지만, 참조 자체를 변경할 수 없기 때문에 객체 자체를 바꿔야 하는 상황에서는 한계가 있을 수 있다.

결론

Java는 함수 호출 시 항상 Call by Value로 동작한다.
하지만 참조 타입 객체를 다룰 때는 주소값을 복사하여 전달하기 때문에, 객체 내부 데이터의 수정은 가능하지만 참조 자체는 변경되지 않는다.
이러한 특성은 Call by Reference 방식으로 동작하는 것 처럼 보이지만, 엄밀히 말해 Java에서는 모든 것이 값 복사를 통해 처리된다는 것을 기억해야 한다.

자바가 왜 Call by Value로만 동작할 수 있는지는 다음 포스트를 참고하길 바란다.
https://velog.io/@ghrltjdtprbs/why-call-by-value

0개의 댓글