[Java] Call by value, Call by reference (feat. new 연산자)

2021. 4. 25. 12:34Backend/☕️ Java

Call by value / reference를 이햐하기 전에 아래의 코드를 먼저 살펴 봅시다.

아래는 두 변수 값을 swap() 하는 예시 코드입니다.

 

class CallByValue 

package callby;

public class CallByValue {
    public static void swap(int x, int y) {
        int temp = x;
        x = y;
        y = temp;
    }

    public static void main(String[] args) {
        int a = 10;
        int b = 20;

        System.out.println("swap() 호출 전: a = " + a + " b = " + b);
        swap(a, b);
        System.out.println("swap() 호출 후: a = " + a + " b = " + b);
    }
}

>>> swap() 호출 전: a = 10 b = 20
>>> swap() 호출 후: a = 10 b = 20

 

 

당연히 swap() 메소드 전후로  a, b의 값이 바뀌어 출력될 것이란 예상을 엎고 동일한 결과가 출력됐습니다.

왜 일까요?

 

이번엔 다른 방식으로 a, b값을 swap() 해보는 코드를 살펴보겠습니다.

 

class CallByRef

public class CallByRef {
    int ref;

    public CallByRef(int ref) {
        this.ref = ref;
    }

    public static void swap(CallByRef x, CallByRef y) {
        int temp = x.ref;
        x.ref = y.ref;
        y.ref = temp;
    }

    public static void main(String[] args) {
        CallByRef a = new CallByRef(10); 
        CallByRef b = new CallByRef(20);

        System.out.println("swap() 호출 전: a = " + a.ref + " b = " + b.ref);
        swap(a, b);
        System.out.println("swap() 호출 후: a = " + a.ref + " b = " + b.ref);
    }
}


>>> swap() 호출 전: a = 10 b = 20
>>> swap() 호출 후: a = 20 b = 10

 

 

처음 살펴본 CallByValue() 클래스와는 다르게, CallByRef() 클래스에선 값이 swap 되어 출력된것을 확인할 수 있습니다.

무엇 때문일까요?

 

이는 자바의 함수(메소드) 호출 방식이 call by value, 즉 값에 의한 호출을 하기 때문입니다. 이 모호한 말을 이해하기 전에, CallByRef 클래스의 main() 함수에서 생성자를 통해 인스턴스를 생성한 과정을 먼저 살펴 보겠습니다.

 

New 연산자: new 연산자는 클래스 따위의 객체 생성 역할

- new 연산자를 통해 1) 메모리(heap 영역)에 데이터를 저장할 공간 할당 받고 2) 그 공간의 참조값(reference)을 객체에 반환 (여기서 객체란 아래 그림의 객체변수, a) 합니다.

- 그렇다면, CallbyRef 생성자를 통해 a 객체 변수는 10 이라는 값이 저장된 참조값(혹은 주소값)을 반환 받게 됩니다. 

그렇기 때문에 아래의 코드에서 a 객체변수를 출력할 땐 참조 주소값이, a.ref를 출력할 땐 우리가 예상한 10이라는 value값이 출력됨을 확인 할 수 있습니다.

 

(여기서 int ref 필드는 CallByRef 클래스 첫줄에 선언되어, 생성자 내부에서 값을 할당 받습니다)

    public static void main(String[] args) {
        CallByRef a = new CallByRef(10);
        CallByRef b = new CallByRef(20);
        System.out.println("a: " + a + ",      a.ref : " + a.ref); 
        // a에는 참조값만 저장돼 있는 것을 확인 할 수 있다 / a.ref에는 10이라는 int형 필드 값이 저장돼 있다.
    }
    
    
    >>> a: callby.CallByRef@681a9515,      a.ref : 10

 

이런 원리로 swap() 메소드 내부를 확인하면,

    public static void swap(CallByRef x, CallByRef y) {
        int temp = x.ref;
        x.ref = y.ref;
        y.ref = temp;
    }
    
    
    // x:  callby.CallByRef@681a9515 x.ref: 10

 

swap() 메소드는 변수 x, y를 스와핑 하는 것이 아닌, x.ref, y.ref 라는 value 값을 스와핑 하여 두 변수의 값을 교환할 수 있게 된다.

 

이로써 우리는 CallByRef 라는 클래스는 메서드 호출시 사용되는 인자의 주소를 넘겨줌으로, swap() 메소드 내부에서 주소를 참조하여 데이터를 변경할 수 있게 됨을 알 수 있습니다. 

이 때, 주소를 참조하여 라는 워딩의 뜻은, 주소에 저장되어있는 값(value)을 참조한다는 뜻이며, 이 값들을 swapping 하여 우리가 원하는 값의 변경을 이뤄 낼 수 있습니다.

 

말이 길었지만, 우리가 swapping 하는 것은 결론적으로 value, 그리하여 자바는 call by Value의 호출방식을 갖게 됩니다.

 

 

Q. 그럼 class CallByValue()도 결국 값에 의한 호출이니, 값의 변경이 있어야 하는 것 아닌가요?

A: 그렇게 생각할 수 있지만, main()메서드 내부에서 정의한 a, b 변수의 값은 결국 main() 메소드의 call by value 를 따르게 됩니다. 

아무리 swap() 메소드의 인자로 들어간다 하더라도, a <-> b 간의 swapping은 swap() 메소드 내부에서만 일어날 뿐입니다.

swap() 메소드가 종료된 이후 a, b 변수는 여전히 main() 메소드의 value이기 때문에 기존의 값을 그대로 유지하게 됩니다.

 

public class CallByValue {
    public static void swap(int x, int y) {
        int temp = x;
        x = y;
        y = temp;

        System.out.println("x: " + x + " y: " + y);
    }

    public static void main(String[] args) {
        int a = 10;
        int b = 20;

        System.out.println("swap() 호출 전: a = " + a + " b = " + b);
        swap(a, b);
        System.out.println("swap() 호출 후: a = " + a + " b = " + b);
    }
}

>>> swap() 호출 전: a = 10 b = 20
>>> x: 20 y: 10
// swap() 메소드 내부에서 x, y값은 swapping되지만, swap() 메소드가 종료되면 x,y는 곧바로 소멸된다.
>>> swap() 호출 후: a = 10 b = 20

 

 

reference

re-build.tistory.com/3

m.blog.naver.com/PostView.nhn?blogId=heartflow89&logNo=220955262405&proxyReferer=https:%2F%2Fwww.google.com%2F