티스토리 뷰

JAVA

[Java] Call by value vs Call by reference

세댕댕이 2022. 10. 7. 11:28

Call by value

-에 의한 호출

- 전달받은 값을 복사하여 처리하며, 전달받은 값을 변경해도 원본이 변경되지 않는다.

- 복사된 변수는 메서드 내부에서 지역적으로만 사용된다.

 

Call by reference

- 참조에 의한 호출

- 전달받은 값을 직접 참조하며, 전달받은 값을 변경하면 원본도 같이 변경된다.

 


우리가 자바를 쓰다보면 메서드의 매개변수로 객체를 넘기고, 메서드 내부에서 객체를 수정했을 때 원본 객체의 값도 같이 변경되는 것을 항상 볼 수 있다.

 

즉, 자바는 메서드 내부에서 객체를 변경했을 때 원본 객체도 같이 변경되니 Call by reference가 이뤄지는 것일까?

 

정답은 아니다!

 

자바에서는 매개변수로 객체를 넘길 때, 객체의 주소 값을 복사해서 넘기기 때문에 원본 객체의 수정이 가능했던 것이다.

 

이게 대체 뭔소리? 그게 결국 주소값을 준다는 이야기 아닌가? 코드로 알아보자..

 

public class Main {
    public static void main(String[] args) {
        Integer num1 = 10;
        Integer num2 = 30;

        swap(num1, num2);

        System.out.println("num1 = " + num1); // 10
        System.out.println("num2 = " + num2); // 30
    }

    public static void swap(Integer arg1, Integer arg2) {
        Integer tmp = arg1;
        arg1 = arg2;
        arg2 = tmp;
    }
}

 

결과를 보면 swap 메서드에 Integer 객체 두개를 넘겨줬고 서로 값을 바꿔줬기 때문에 num1 = 30, num2 = 10이 되어야 할 것 같지만 막상 결과는 예상과 다르게 원본 객체의 값이 변경되지 않은 것을 확인할 수 있다.

 

왜 그런지 그림을 통해 살펴보자..

 

 

Integer num1 = 10;
Integer num2 = 30;

선언하게 되면 힙 영역에 10, 30을 가진 Integer 객체가 각각 생성되고, 스택 영역에 생성된 변수에 각각의 참조값(주소값)을 저장한다.

 

public static void swap(Integer arg1, Integer arg2) {
    ...
}

num1 객체를 arg1에, num2 객체를 arg2에 매개변수로 전달한다.

이때 arg1은 num1이 가지고 있던 주소값 (10을 가리키는 중)을 복사해서 가지고,

arg2는 num2가 가지고 있던 주소값 (30을 가리키는 중)을 복사해서 가진다.

 

다시 말하면 매개변수가 화살표(포인터)를 새로 만들어서 똑같은 객체를 가리키고 있도록 만들어 준 것이다. 

그러니까 원본 객체의 포인터와 매개변수의 포인터는 서로 독립적으로 움직인다.

 

** (주의) 자바는 포인터를 사용하지 않는다! 근데 개인적으로 포인터 용어가 익숙해서 포인터라고 부름 

Call by value

만약 Call by reference였다면?

 

만약 Java가 Call by reference였다면?

이런 식이 되지 않을까? (확실치 않음)

 

 

아무튼 다시 Call by value로 돌아와서, 

arg1 = arg2;

를 하게 된다면?

 

 

단순히 arg1의 포인터가 arg2가 가리키고 있는 30 객체를 똑같이 가리키게 된다.

여기서 핵심은 "원본 변수인 num1의 포인터는 여전히 10 객체를 가리키고 있다"는 것이다.

 

그렇기 때문에 swap 메서드를 통해 내부에서 arg1과 arg2의 값을 변경한다 하더라도, 

원본 객체인 num1과 num2의 포인터에는 아무런 영항을 끼치지 못한다.

즉 Call by value의 특징인, 지역적으로 사용된다는 성질이 그대로 반영되고 있는 것이다.

 

다만 매개변수에서 객체의 값을 변경하면 원본의 값도 변경된다.

왜? 당연히 둘다 같은 객체를 가리키고 있으니까!!!

이것때문에 자바가 Call by reference가 아닌가 하고 혼동을 불러일으키게 된다.

 

 

그냥 정리하면 원본 변수와 매개변수가 각자의 독립적인 포인터를 가지고 있기 때문에 둘 간은 서로 영향을 주지 못한다고 생각하면 이해가 좀 되는 것 같다.

 


+ 토막 상식) 자바에는 포인터 개념이 없는 이유가 무엇일까?

자바는 포인터 대신 참조(reference)의 개념을 사용하기 때문이다. 

 

참조나 포인터나 둘 다 객체의 주소를 가리키고 접근할 수 있다는 점은 똑같다.

하지만 둘의 핵심적인 차이는, "포인터는 메모리 주소를 직접 다룰 수 있지만, 참조는 할 수 없다"는 것이다.

쉽게 말하면 참조는 그냥 Read-only 같은 느낌이다.

 

메모리 주소를 개발자가 직접 다루는 것은 아주 강력하다. 다만, 강력한만큼 위험성이 따르기 마련이다.

C언어를 해봤다면 포인터라는 장벽에 부딪혀 머리가 녹아내리는 경험을 반드시 겪게 될만큼 복잡하고 위험한 녀석이다..

 

자바는 강력한 성능은 조금 포기하는 대신 안정성 및 편리한 개발을 위해 포인터라는 개념을 포기하고 단순히 접근만 가능한 참조라는 개념을 사용한다.

 

조금 더 상세히 들어가면 GC가 실행될 때 마다 힙 영역 객체의 주소가 계속 변경되기 때문이라고 하는데, 솔직히 거까지는 그냥 그런갑다 하면 될 것 같다.. ㅎ


(참고)

https://deveric.tistory.com/92   

https://sorjfkrh5078.tistory.com/278  

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함