본문 바로가기

JAVA

[JAVA] 자바는 항상 Call-by-value로 동작한다?

728x90
자바를 처음 공부하면서 call-by-value와 call-by-reference에 관한 궁금증이 생겼다.
이에 관해 공부한 기록이다.

Call-by-value vs Call-by-reference

두 개념은 어떻게 다를까?

두 개념 모두 변수가 호출되는 방식에 관한 개념이다.

  • Call-by-value는 호출자와 피호출자가 각각 독립적인 두 변수를 가진다.
  • Call-by-reference는 호출자와 피호출자가 동일한 변수를 사용한다.

호출자와 피호출자가 동일한 변수를 사용한다는 것은 조금 이해하기 힘든 표현일 수 있다.

그 이유는 Call-by-reference라는 개념 자체가 포인터와 결부되어 있기 때문이다.

 

내가 이해하기로는 Call-by-value는 그릇에 비유될 수 있다.

Stack 메모리에 변수가 쌓이고 그 변수에는 값이 담긴다(할당).

때문에 해당 변수가 호출된다면(피호출자가 된다면) 변수를 호출한 호출자(지역 변수)에 동일한 값이 담기는 것이다. 

 

하지만 Call-by-referenc는 보다 추상적인 개념으로, 변수보다는 해당 주소에 담기는 것으로 이해했다.

이는 메모리에 직접 접근할 수 있기 때문에 실현될 수 있는 개념으로

특정 주소의 값을 변수라는 그릇에 담는 것이 아닌 특정 주소에 들어갈 값을 지정하는 방법인 것이다.

 

Call-by-value ~ Reference type

  • Reference type은 Heap 메모리에 생성된 객체나 배열 등을 가리키는 데이터 타입이다.
  • Call-by-value는 특정 변수가 가지는 값이 호출됐을 때 호출자가 해당 값의 복사본을 할당받는 방식이다.

이름과 동작 방식만 봤을 때는 Reference type이 Call-by-reference로 동작하는 것이 자연스러워 보인다.

하지만 위에서 언급한 대로 Call-by-reference는 메모리에 직접 접근해 특정 메모리에 들어갈 값을 지정해야 한다.

 

예제를 보자

public class CallByReference {
    public static void main(String[] args){
        int[] arr={1,2,3};
        callByReference(arr);
        for(int i:arr)System.out.println(i);
    }

    public static void callByReference(int[] arr){
        arr=new int[]{3,2,1};
    }
}

Call-by-reference로 동작한다면 새로 생성된 {3,2,1} 배열이 출력되어야 한다.

하지만 실행결과는 피호출자인 {1,2,3}이 출력된다.

 

그렇다면 반대로 일반적인 Call-by-value로 동작한다고 가정해 보자.

Call-by-value로 동작한다면 호출자와 피호출자가 완전히 독립된 두 변수여야 한다.

따라서 호출자가 피호출자의 본래 값에 영향을 줄 수 없어야 한다는 것을 의미한다.

public class CallByValue {
    public static void main(String[] args){
        int[] arr={1,2,3};
        callByValue(arr);
        for(int i:arr)System.out.println(i);
    }

    public static void callByValue(int[] arr){
        arr[0]=10;
    }
}

그렇다면 위의 코드의 출력값은 arr가 callByValue 메서드의 영향을 받지 않았기 때문에 {1,2,3}이 출력되어야 한다.

하지만 출력값은 메소드의 영향을 받은 {10,2,3} 배열이 출력된다.

 

그렇다면 도대체 어떤 방식으로 동작하는 걸까?

Call-by-sharing

위의 상황을 정리해 보면,

  • 호출자에 새로운 값이 할당되었을 때 피호출자에 영향을 주지 않기 때문에 Call-by-reference는 아니다.
  • 호출자는 피호출자와 같은 배열을 가리키고 있다. 따라서 호출자에서 배열에 접근해서 배열 내부를 변경시키면, 
    피호출자에도 그 수정이 반영된다. 

따라서 아래가 참인 것을 알 수 있다.

호출자와 피호출자가 Reference type일 때, 같은 주소값을 가진다.

객체의 복사본을 가지는 것이 아닌 같은 주소값을 value로 한 Call-by-value 방식인 것이다.

그렇게 되면 위 두 상황이 설명이 된다.

 

예제를 보자

public class CallBySharing {
    public static void main(String[] args){
        int[] arr={1,2,3};
        callBySharing(arr);
        for(int i:arr)System.out.println(i);
        //10 2 3
    }

    public static void callBySharing(int[] arr){
        arr[0]=10;
        //같은 배열을 가리키는 시점이므로 피호출자의 첫 번째 원소도 10으로 바뀜
        arr=new int[] {3,2,1};
        //서로 다른 배열을 가리키게 됨
        arr[0]=10;
        //피호출자는 영향을 받지 않음
    }
}

이처럼 호출자가 피호출자의 값을 복사해서 두 변수가 완전히 독립적으로 동작하는 일반적인 Call-by-value 방식이 아닌

호출자가 피호출자에 담긴 주소값을 복사해서 같은 객체를 가리키는 방식을 Call-by-sharing이라고 한다.

 

참고

https://stackoverflow.com/questions/12757841/are-arrays-passed-by-value-or-passed-by-reference-in-java

 

Are arrays passed by value or passed by reference in Java?

Arrays are not a primitive type in Java, but they are not objects either, so are they passed by value or by reference? Does it depend on what the array contains, for example references or a primitive

stackoverflow.com

https://stackoverflow.com/questions/373419/whats-the-difference-between-passing-by-reference-vs-passing-by-value/430958#430958

 

What's the difference between passing by reference vs. passing by value?

What is the difference between a parameter passed by reference a parameter passed by value? Could you give me some examples, please?

stackoverflow.com

 

728x90

'JAVA' 카테고리의 다른 글

[JAVA] 상속과 조합  (1) 2023.11.08
[JAVA] VO(Value Object), 원시값 포장  (0) 2023.10.28
[JAVA] equals(), hashCode() 재정의(동등성과 동일성)  (0) 2023.10.27
[JAVA] JVM 메모리 영역  (0) 2023.06.13