본문 바로가기

Java/문법 및 이해

[JAVA] Call by value, Call by reference의 이해

자바를 사용하다보면 메서드에 값(primitive type)을 전달하는 것과 객체(reference type)를 전달하는 것과는 매우 큰 차이가 있다. 

 

원시 타입과 참조 타입을 구별하고 이해하고 사용하는 것은 정말이지 너무나도 중요하다.

 

 

Call by value

메서드를 호출할 때 값을 넘겨준다. 즉, 메서드를 호출하는 호출자(Caller)의 변수와 당하는 수신자(Callee)픠 파라미터는 복사된 서로 다른 변수이다.

값만을 전달하기 때문에 수신자와 파라미터를 수정해도 변수에는 아무런 영향이 없다.

 

Call by reference

참조(주소)를 직접 전달한다.

참조를 넘기기 때문에 호출자의 변수와 수신자의 파라미터는 완전히 동일한 변수이다.

메서드 내에서 파라미터를 수정하면 그대로 원본 변수에도 반영된다.

 

 

그러면!

자바에서 파라미터를 전달하는 방법은 무엇일까?

자바에서 변수를 넘기고 거기서 값을 수정하는 것은 Call by value이다.

 

 

JVM 메모리에 변수가 저장되는 위치

Java 의 Call by Value 에 대해 이해하기 위해선 먼저 변수 생성 시 메모리에 어떤 식으로 저장되는 지 알아야 합니다.

Java 에서 변수를 선언하면 Stack 영역에 할당됩니다.

 

원시 타입 (Primitive Type) 은 Stack 영역에 변수와 함께 저장되며

참조 타입 (Reference Type) 객체는 Heap 영역에 저장되고 Stack 영역에 있는 변수가 객체의 주소값을 갖고 있습니다.

 

 

아래의 코드를 보자

class Updater {
    void update(int count) {
        count++;
    }
}

class Counter {
    int count = 0;
}

public class Sample {
    public static void main(String[] args) {
        Counter myCounter = new Counter();
        System.out.println("before : " + myCounter.count);

        Updater myUpdater = new Updater();
        myUpdater.update(myCounter.count);
        System.out.println("after : " + myCounter.count);
    }
}

 

본래라면 하나의 자바 파일에 2개 이상의 클래스를 선언하는 방식은 좋은 방식이 아니다.

 

아무튼

 

Updater 클래스는 전달받은 정수형 변수를 1만큼 증가시키는 update라는 메서드를 가지고 있다.

 

Counter 클래스는 count라는 객체변수를 가지고 있다.

 

위 코드의 의도는 main 메서드에서 Counter, Updater 클래스에 대한 객체를 생성하고 count 값을 Updater 클래스의 객체를 통해 값을 증가시키려고 하는 코드이다.

 

하지만 위 코드를 실행하면 결과는 다음과 같다.

before : 0
after : 0

왜 이런 결과가 나올까?

본인은 after에 1이 증가한다고 생각했다.

 

그 이유는 Updater 클래스 내의 update() 메서드에서 사용한 매개 변수(count)는 메서드 안에서만 쓰여지는 변수이기 때문이다.

void update(int count) {} 메서드에서 매개 변수 count는 메서드 안에서만 사용하면 count라는 이름의 변수이기에 main() 메서드에서의 count 매개 변수는 아무런 영향을 받지 않는다.

 

메모리와 연관지어서 설명하면,

main 메서드에서 stack 메모리가 생성되고, Counter 클래스도 똑같이 새로운 stack 메모리가 생성된다. 당연히 Updater도 마찬가지이다.

그렇다면 Counter 클래스의 stack 메모리 안에는 count 변수가 있을 것이다. Updater 의 stack 메모리에서도 전달받은 count 변수가 있는데 서로 다은 stack 메모리이기에 아무런 영향을 주지 않는다. 오히려 메서드가 끝나면 stack 메모리는 소멸된다.

 

아래는 이러한 문제점을 해결한 코드이다.

class Updater {
    void update(Counter counter) {
        counter.count++;
    }
}

class Counter {
    int count = 0;
}

public class Sample {
    public static void main(String[] args) {
        Counter myCounter = new Counter();
        System.out.println("before update:"+myCounter.count);
        Updater myUpdater = new Updater();
        myUpdater.update(myCounter);
        System.out.println("after update:"+myCounter.count);
    }
}

보면 Updater 클래스의 update 메서드에 int count와 같은 primitive type이 아닌 Counter count와 같은 객체 reference type으로 변경하였다.

 

또한, myUpdater update(myCounter) 메서드 호출 부분도 수정하였다.

before update:0
after update:1

 

 

 

 

 

 

꼭 참고하시길.

[참고]

https://bcp0109.tistory.com/360