본문 바로가기

IT/Java

자바 pass by value pass by reference?

◆ 매개변수/ 인자의 정의


  1) 매개변수(Parameter) - 함수(메소드)를 선언할 때 전달하는 변수

  2) 인자(Argument) - 함수(메소드)를 호출(실행)할 때 전달하는 변수

int main()
{
	int argument = 5;
	test(argument);
	return 0;
}

void test(int parameter){
	
}



 인자전달 방식


 1) call-by-value 

- 메서드 호출 시 기본 자료형의 값을 인자로 전달하는 방식

- 호출 시의 실인자는 별도의 값으로 인식되어 영향을 받지 않음


 2) call-by-reference

- 메서드 호출 시 전달하려는 인자를 참조(객체) 자료형을 사용한 경우를 의미

- 하나의 객체를 참조하는 변수가 2개가 되어 어느 한 곳에서 수정을 하게 되면 같은 객체를 참조하는 다른 쪽에서도 영향을 받게 됨.



 

 ◆ 자바에서의 값 변경 사례

HelloWorld{

	public static void main(String[] args) {

		HelloWorld hw = new HelloWorld();
		TargetClass tc = new TargetClass();

		tc.setText(new String("처음 입력 값입니다."));
		System.out.println(" 변경 전 : " + tc.getText() + " " + tc.hashCode());

// 출력: 변경 전 : 처음 입력 값입니다. 1704856573

hw.changeObject(tc); // tc 변경 시도                  // tc에 변화가 없다면 call by value일테고, 변화가 있다면 call by reference System.out.println(" 변경 후 : " + tc.getText() + " " + tc.hashCode());

// 출력: 변경 후 : 처음 입력 값입니다. 1704856573

} public void changeObject(TargetClass obj) { obj = null; // 넘어온 인자를 null 처리함. } } public class TargetClass { private String param; public String getText() { return param; } public void setText(String param) { this.param = param; } }


- 자바 레퍼런스 타입이 정말 call by reference에 의한 전달이라면 HelloWorld(TargetClass obj)이 수행되고 나면 tc는 null 이어야 함.

- 실제로 자바도 reference type을 인자로 전달할 때에 reference를 넘기지만 그 reference(주소값)가 아니라 reference의 사본의 값을 넘기므로 진정한 의미에서 call by reference로 볼 수 없음




 ◆ 자바에서의 오해의 소지 

public class CallyByXXXTest {

	public static void assignNewPerson(Person p) {
		p = new Person("Bob");
	}
	
	public static void main(String[] args) {

		Person sam = new Person("Sam");
		assignNewPerson(sam);
		System.out.println(sam);
		System.exit(0);

	}
}

public class Person {

	private String mName;

	public Person(String name) {
		mName = name;
	}

	public void setName(String name) {
		mName = name;
	}

	@Override
	public String toString() {
		return "Person " + mName;
	}
}

-  Person 객체를 넘겨받아 새로운 Person 객체로 변경하는 메서드 호출 코드

-  sam 객체를 생성한 후 assignNewPerson 메서드로 전달하고 메서드 종료 후 sam 객체가 bob 객체로 변경되었는지 확인해보았지만 아무런 변화 없음


public class CallyByXXXTest {

	public static void changePersonName(Person p) {
	    p.setName("Bob");
	}

	public static void main(String[] args) {
	  Person sam = new Person("Sam");
	  changePersonName(sam);
	  System.out.println(sam);
	  System.exit(0);
	}
}

-  오해의 소지 발생

참조변수는 임의의 객체에 대한 레퍼런스를 저장하므로 메서드로 전달한 값이 레퍼런스임

답변: 전달된 레퍼런스는 참조변수 sam  자체의 레퍼런스(주소값)가 아닌 sam이 저장하고 있는 값(이것도 레퍼런스)임. 만약 자바가 call-by-reference 를 지원한다면 참조변수 sam 자체의 레퍼런스(주소값)을 얻을 수 있는 방법이 있어야 하나 자바는 이 방법을 지원하지 않음

-  결론: 참조변수 sam에 저장된 값(다른 객체의 레퍼런스 값)을 복사하여 파라메터 p로 넘겨준 call-by-value가 맞음




 ◆ 정리


다음은 자바 인어넛셀 개정3판(오라일리 2000)에 있는 내용입니다.


  자바가 '레퍼런스에 의해(by reference)' 배열과 객체를 처리한다고 설명하였다. 이를 '레퍼런스에 의한 전달(pass by reference)'와 혼동하지 말기를 바란다. '레퍼런스에 의한 전달'은 몇몇 프로그래밍 언어의 메소드 호출 규약을 설명하는데 사용되는 용어이다.

  '레퍼런스에 의한 전달' 언어에서는 값(심지어 기본값이라고 하더라도) 이 메소드에 바로 전달되지 않는다. 대신에, 메소드는 레퍼런스를 값으로 전달받는다. 그러므로 메소드가 매개변수를 변경한다면, 이 변경은 메소드가 리턴했을 때

반영 되어 있을 것이며, 매개변수가 기본형이라도 이는 마찬가지이다. (여기서 기본형이란 자바의 primitive type을 말하는 것)


자바는 이렇게 하지 않는다. 자바는 '값에 의한 전달' 언어이다. 그러나 레퍼런스 타입의 경우에는 전달되는 값이 레퍼런스이다. 이것이 '레퍼런스에 의한 전달'과 똑같은 것은 아니지만 말이다.


** 오브젝트인 파라메터를 전달 할 경우, 

(1) 그 오브젝트가 가지고 있는 값을 변경하는 것은 가능

(2) 하지만 new를 이용해 새로 생성하거나, 다른 오브젝트를 대입한다해도 원래 파라메터의 값은 해당 메소드 밖에서는 그대로임









참고사이트

http://mussebio.blogspot.kr/2012/05/java-call-by-valuereference.html

http://www.yunsobi.com/blog/480

http://okky.kr/article/32431