티스토리 뷰

Java

call by value, call by reference

LichKing 2015. 11. 25. 10:06

메서드를 호출할때 호출 하는 메서드가 인자를 필요로 하는경우 () 안에 인자를 넣어 호출한다. 이때 메서드에서 파라미터를 받아 사용할때 호출하는 방법이 2가지가 있는데

그것이 call by value, call by reference 이다.

우리말로하면 값호출, 참조호출 정도가 될것이다.

호출방법을 프로그래머가 지정할 수는 없다. 동일한 인자를 여기선 값으로, 저기선 참조로 호출할수는 없다는 얘기다. 기본적으로 자바는 8가지의 기본형 데이터에 대해선 call by value로 호출하며 그 외에 사용자지정타입. 즉 객체에 대해서만 call by reference로 호출하게된다.

 

두가지의 차이점은 소스로 확인해보자

 

 class TestClass{

    int num1;

    int num2;

 

    public TestClass(int num1, int num2){

      this.num1 = num1;

      this.num2 = num2;

    }

  }

 

public class Test{

  public static void main(String[] arg){

    int num = 100;

    int num2 = callValue(num);

 

    System.out.println(num);   //1

    System.out.println(num2);  //2

 

    TestClass tc = new TestClass(5, 10);

 

    TestClass tc2 = callReference(tc);

 

    System.out.println(tc.num1);  //3

    System.out.println(tc2.num1);//4

    System.out.println(tc == tc2);//5

  }

 

  public static int callValue(int methodNum){

    methodNum+= 10;

    return methodNum;

  }

 

  public static TestClass callReference(TestClass methodTc){

    methodTc.num1 += 10;

 

    return methodTc;

  }

}

 

1번 출력문은 메인메서드에서 선언한 num, 2번 출력문은 num을 인자로보내고 반환된 num2를 출력했다.

두개의 값은 서로 다르다.

3번 출력문은 TestClass의 인스턴스인 tc의 num1을 출력하고 4번 출력문은 그 tc를 인자로보내 반환받은 인스턴스 tc2의 num1을 출력했다.

tc2야 그렇다쳐도 tc는 생성자 이외에선 건드린부분이 없는데 값이 바뀌어있고 결과적으로 두 출력문의 값은 동일하다.

 

메서드명만 봐도 알수있겠지만 전자가 call by value, 후자가 call by reference 이다.

값에 의한 호출은 인자가 가지고있는 값 그 자체를 복사하게된다. 그리고 메서드는 그런 복사된 값을 들고 메서드 내부에서 실행되게되는것이다.

처음에는 메인메서드의 num과 callValue() 메서드의 methodNum이 같았지만 메서드가 진행되면서 callValue()의 인자인 methodNum은 가공이 되게되고 그런 가공된 값을 반환한다. 이때 이미 값 자체가 복사되어 진행된것이기때문에 메인메서드의 num은 아무런 연관이 없다. 그래서 서로의 값이 다르게되는것이다.

 

두번째 callReference() 메서드에 대해 살펴보자.

tc는 참조형 변수로 heap에 생성된 객체의 주소를 가지고있는 변수다. 해당 변수를 메서드에 인자로 보내 callReference() 메서드 내부의 methodTc로 사용중이다. 여기서 생각해봐야할것은 애초에 tc는 객체에 대한 참조값을 갖고있던 변수라서 그것이 그대로 복사되어 메서드에 전달되어도 methodTc도 역시 같은 객체를 참조하게 되는것이다.

서로 다른 영역의 두 변수가 같은 객체를 참조하다보니 한쪽에서는 변경한부분이 전혀없는데도 불구하고 3번과 4번 출력이 동일한 결과를 보이는것이다.

추가적으로 5번 출력을 확인해보면 두 변수가 서로 같은 객체를 참조중이라는것을 확인할 수 있다.

이처럼 의도치않게 값이 변경될 수 있으므로 객체를 인자로 보낼때는 좀 더 주의가 필요하지만 잘쓰면 그만큼 더 편하게 쓸수도있다.

 

추가적으로 자바에서는 무조건 call by value라는 말도 있다. 개인적으로는 용어를 정확히 쓰고자한다면 이말이 좀더 맞는게 아닐까 라고 생각하는데 이건 무슨말인지 확인해보자.

일단 자바에서는 포인터가없다. 때문에 주소값 그 자체를 컨트롤할수 있는법이 없다.

참조형 변수가 주소값을 저장한다고 하지만 좀 더 정확히 말하면 주소를 저장하는게 아니라 '주소를 가리키는 레퍼런스' 를 저장하고있는것이다. 그리고 메서드를 호출하게되면 call by value와 마찬가지로 이 레퍼런스가 복사되어 인자로 전달되는것이다. 즉 주소 그 자체를 '복사없이' 인자로 전달하는게 아니라 기본형 변수나 참조형 변수가 어쨋든 자기자신이 갖고있는 값을 복사해서 전달하는것이기때문에 결과적으로는 call by value라는 것이다.

좀더 쉽게 이해하기위해 예제소스를 보자

 

public class Test{

  public static void main(String[] arg){

    String str = "abc";

    String str2 = callTest(str);

 

    System.out.println(str);

    System.out.println(str2);

  }

 

  public static int callTest(String methodStr){

    methodStr = "";

    return methodStr;

  }

}

 

String은 자바 기초자들이 가장 먼저 접하는 참조형 변수일것이다. 두 출력을 확인해보면 결과값이 다른걸 볼수있다. 왜일까?

메서드로 처음 전달받았을때 methodStr과 str은 서로 같은 "abc"라는 문자열이 저장된 상수영역을 가리키고있다가 methodStr을 수정할경우 methodStr이 가리키는 주소가 바뀐것이다.

서로 가리키는 주소가 바뀌기때문에 출력의 결과가 다른것이다.

어떻게보면 이건 지극히 정상이라고 생각할수도있으나 진정한 의미의 call by reference라면 참조형 변수는 '주소를 가리키는 레퍼런스'를 저장하고있는게 아니라 '주소 그자체'를 저장하고있어야한다. 주소 그 자체를 저장하고있기때문에 메서드에서 ""로 값을 바꾸면 해당 주소의 값이 변경되어 결국 두 출력이 같아야한다는것이다.

난 C++에 대해선 아는게 없어 자바로 설명하고있지만 C++에서는 실제로 그렇게 작동이 되는듯하다. 결국 개발자가 직접 주소에 접근하는 방법이 전혀 없는 자바는 완벽한 의미의 call by reference를 사용할 수 없어 전부다 call by value라는 말이다.

 

개인적으로는 전부다 call by value라고 보는것이 맞다고 생각한다. 하지만 실제 강의 현장에서 객체의 전달을 call by reference라고 설명하고있기때문에 정상적인 의사소통을 위해서 자바에서 call by reference라는 말이 나오면 '아 객체전달이구나' 하고 이해하면 될것이다.

'Java' 카테고리의 다른 글

tiles3 설정  (0) 2016.02.18
String Class와 equals()  (0) 2015.12.02
call by value, call by reference  (0) 2015.11.25
현재시간구하기  (0) 2015.06.06
The superclass "javax.servlet.http.HttpServlet" was not found on the Java Build Path  (27) 2015.01.24
파일 다운로드  (0) 2014.12.23
공유하기 링크
TAG
댓글
댓글쓰기 폼