본문 바로가기
Study/Java

[Java] Call by value?

by 검프 2021. 4. 28.

자바에서는 모두 Call/Pass by value.

자바가 Call by Reference가 아니라는 사실에 적잖이 충격이었던 경험이 있어요.

제게 Call by Value라고 확신을 하게 해줬던 문서들을 첨부해요.

 

우선 래퍼런스의 끝판왕 oracle docs를 확인해볼게요

https://docs.oracle.com/javase/tutorial/java/javaOO/arguments.html

문서에서 프리미티브 타입의 인자 잔달에 대해 확인해볼 수 있어요.

"Passing Primitive Data Type Arguments"

int와 double와 같은 프리미티브 타입은 메서드 인자(argument)에 값으로 전달된다고 해요.(이는 call-by-value, pass-by-value로 불려요)

전달될 때 아래 그림과 같이 값이 복사되고 넘어간다고 생각하면 편할거 같아요.(직접 그리기 힘드네용)

https://user-images.githubusercontent.com/48986787/116268623-742f2580-a7b8-11eb-8bfe-7ba7da318a30.png

즉, 메소드 인자로 들어온 값의 변경은 메서드 내에서만 이루어져요( 호출한 메서드의 스코프를 가져요 )

이미 복사가 이루어진 후 이기 때문에 변경이되도 본래의 값에는 영향을 미치지 않는거죠.

"Passing Reference Data Type Arguments"

래퍼런스 타입도 프리미티브 타입과 같이 메서드의 인자에 값으로 전달된다고해요. (이부분이 정말 어려워요)

https://user-images.githubusercontent.com/48986787/116269181-f15a9a80-a7b8-11eb-8c56-3b52ce6617b2.png

그림으로만 봤을땐, 참조하는 객체를 넘겨주는것이 아닌가 싶지만, heap의 값(실제 객체)이 아닌, stack의 값(힙 메모리 영역의 주소)이 넘어가요.

즉, copyNumber가 메서드의 인자로 넘어가는데, 이는 복사된 힙메로리 영역 주소(stack의 값)가 넘어가는거에요.

그렇기에 pass by value라 할 수 있는 거에요.

 

저도 예전에 여기까지 설명으론 이해하지 못했던거 같아요.

이부분은 https://stackoverflow.com/questions/40480/is-java-pass-by-reference-or-pass-by-value/12429953#12429953

의 답변을 요약해봤어요.

public class Main {

     public static void main(String[] args) {
          Foo f = new Foo("f");
          changeReference(f); // It won't change the reference!
          modifyReference(f); // It will modify the object that the reference variable "f" refers to!
     }

     public static void changeReference(Foo a) {
          Foo b = new Foo("b");
          a = b;
     }

     public static void modifyReference(Foo c) {
          c.setAttribute("c");
     }
}

위와 같은 코드가 있다고 해볼게요.

Foo f = new Foo("f");
  1. 이 과정에서 스택에 메모리 주소가, 힙에는 객체가 올라가요.

https://user-images.githubusercontent.com/48986787/116270242-e3f1e000-a7b9-11eb-8b49-1801ad7f58a8.png

public static void changeReference(Foo a)
  1. 이 메서드가 선언될 때 아래와 같이 진행이 되요. ( 아직 호출이 이루어 지지 않았기에 이때 a는 null이에요 )

https://user-images.githubusercontent.com/48986787/116270428-0ab01680-a7ba-11eb-8967-ce72da5f37c2.png

changeReference(f);
  1. 메서드가 호출됐어요. 이때는 아래와 같아요.

https://user-images.githubusercontent.com/48986787/116270608-34693d80-a7ba-11eb-80b6-99b655ded698.png

public static void changeReference(Foo a) {
    Foo b = new Foo("b");
  1. 새로운 객체를 생성했어요. 이때의 그림은 아래와 같아요.

https://user-images.githubusercontent.com/48986787/116270814-64184580-a7ba-11eb-8df8-76515fe2eaa3.png

public static void changeReference(Foo a) {
  Foo b = new Foo("b");
  a = b;
}
  1. a가 b에 의해 복사가 돼요. 이떄의 그림은 아래와 같아요

https://user-images.githubusercontent.com/48986787/116271101-a3df2d00-a7ba-11eb-9808-c6d5497de7ed.png

이렇게 변경이 됐기에. 인자로 넘어온 f에는 아무런 영향이 없었던 거에요.

public static void modifyReference(Foo c) {
      c.setAttribute("c");
}
  1. modifyReference는 참조하는 메모리 주소의 값을 변경시켜요

https://user-images.githubusercontent.com/48986787/116271514-01737980-a7bb-11eb-94c0-afeb0203a07e.png

위와 같기에 인자로 넘어온 f의 값이 변경됐던거에요.

댓글