개발은 아름다워

[ Java ] Java는 왜 callbyValue만 되는걸까? 본문

자바

[ Java ] Java는 왜 callbyValue만 되는걸까?

do_it_zero 2024. 10. 24. 16:47

call by value
call by reference

이 두가지 용어는 함수가 매개변수를 받을때 어떤 방식으로 받는가이다.

call by value

int a = 3;

void swap(int x) {
x = 5;
}

swap(a)라면 a의 값은 어떻게 될까?
a = 3 그대로이다.

#include <stdio.h>

void swap(int x) {
    x = 5;
}

int main() {
    int a = 3;

    printf("Before swap: %d\n", a);

    // swap 함수를 호출하더라도 a의 값은 변경되지 않음
    swap(a);

    printf("After swap: %d\n", a);

    return 0;
}

int x에 a의 값 자체가 복사 되었기 때문이다.

call by reference

나는 함수를 통해서 a의 값을 변경하고 싶다면 어떻게 하면 될까?

int a = 3; 이라는 것은 a라는 변수라는 변수에 3이라는 값을 넣었다는 뜻이다. a의 주소값은 따로 존재한다.

변수 주소 값을 찾아내서 주소에 다른 값을 넣어주는 방법을 통해 변경이 가능하다.

void swap(int x) {
x = 3;
}

여기서 int x가 아니라 주소값을 받는 변수인 포인터를 써서
void swap(int p) {
p = 5;
}

함수의 매개변수를 바꾼 후

함수에 전달하는 인자도 바꿔야한다.
swap(a) 가 아니라 swap(&a) 이렇게 바꾸면 포인터는 a라는 변수의 주소값을 받게 된다.

그리고 함수를 통해 그 주소값에 5이라는 값을 할당하여, 결국 int a에는 5라는 값이 남게 된다.

#include <stdio.h>

void swap(int* p) {
    *p = 5;
}

int main() {
    int a = 3;

    printf("Before swap: %d\n", a);

    // swap 함수를 호출하여 포인터를 통해 a의 값을 변경
    swap(&a);

    printf("After swap: %d\n", a);

    return 0;
}

swap(&a) 이면 a는 3이 아닌 5가 된다.

int a = 3; 인경우
두가지의 값이 생긴다. a라는 변수의 주소값과 a라는 변수에 할당되는 3이라는 값이다.

함수의 매개변수는 값을 복사한다. 매개변수가 어떤 값을 복사하느냐에 따라 두가지로 나뉘는 것이다.

call by value : 매개변수가 전달 받는 변수의 값을 복사하는 것
call by reference : 매개변수가 전달 받는 변수 자체의 주소 값을 복사하는 것

그렇다면 자바는??

위에 내용을 간단하게 정리하자면

int a = 3; 인경우
두가지의 값이 생긴다. a라는 변수의 주소값과 a라는 변수에 할당되는 3이라는 값이다.

함수의 매개변수는 값을 복사한다. 매개변수가 어떤 값을 복사하느냐에 따라 두가지로 나뉘는 것이다.

call by value : 매개변수가 전달 받는 변수의 값을 복사하는 것
call by reference : 매개변수가 전달 받는 변수 자체의 주소 값을 복사하는 것

자바는 call by value만 가능하다.

package Exam;

public class Car {
    private String name;

    public Car(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return this.name;
    }
}
package Exam;



public class Exam {
    public static void main(String[] args){

        Car car1 = new Car("벤츠");
        Car car2 = new Car("모닝");

        swap(car1,car2);

        System.out.println("//함수에서는 매개변수가 값을 복사만 것임을 알 수 있음. 따라서 car1과 car2의 가리키는 값에 변화가 없음//");
        System.out.println("결과는 바뀌지 않음  car1 벤츠 : " + car1); // 결과는 바뀌지 않음  car1 벤츠 : 벤츠
        System.out.println("결과는 바뀌지 않음 car2 모닝 : " + car2);  // 결과는 바뀌지 않음 car2 모닝 : 모닝

        Car temp;
        temp = car1; // temp에 car1이 가리키는 객체의 주소값이 할당됨
        car1 = car2; // car1에는 car2가 가리키네는 객체의 주소값이 할당됨
        car2 = temp; // car2에는 temp가 가리키는 객체의 주소값이 할당됨

        System.out.println("// 메소드에서 매개변수가 값을 복사 받는 것이 아닌 직접 값을 변경 ///");

        System.out.println("결과는 바뀜! 원래는 car1 벤츠 : " + car1);
        System.out.println("결과 바뀜! 원래는 car2 모닝 : "+ car2);


    }
    
    public static void swap(Car c1 , Car c2) {
        Car temp;
        temp = c1;
        c1 = c2;
        c2 = temp;
    }


}

swap을 해도 car1과 car2의 값이 바뀌지 않은 이유는 값에 의한 복사였기 때문이다.
메소드 내에서가 복사된 값의 변경이 아닌, 직접 참조타입 변수의 값을 변경하면 car1과 car2가 가리키는 객체의 값이 바뀜을 알 수 있다.

왜 자바가 call by reference라고 혼동하는걸까??

내가 생각할때는 reference 주소값 복사라는 단어 때문에 헷갈려하는 것 같다. 내가 그랬다.
이걸 확실히 머리속에 넣으려면 어떤 주소값을 복사하는지 생각해보면 된다.
call by reference는 매개변수로 전달되는 변수의 주소값을 참조한다.

만약 call by reference였다면 c1은 car1의 자체의 주소값을 복사했어야한다.
하지만 c1은 car1이 가리키는 객체의 주소값을 복사한다.이것은 그냥 참조 변수 car1의 값을 복사했을 뿐이고, car1의 값이 객체의 주소값일 뿐이다. 즉, 값을 복사했뿐이고 그 값이 객체의 주소값이였을 뿐이다.

그러면 자바는 call by refernece가 안되는 것인가? 근본적으로 할 수가 없다. 왜냐하면 변수 자체의 주소값에 접근할 수 없게 만들었기 때문이다.

Java는 명시적인 포인터 개념이 없다. 포인터는 메모리 주소를 직접 다루는 것이 가능한데, Java는 이러한 메모리 주소에 대한 직접적인 조작을 허용하지 않고, 메모리 관리를 추상화하여 제공한다.
Java의 메모리 관리는 가비지 컬렉션(Garbage Collection)이라는 기술을 사용하여 이루어진다.