Java에서 모든 것은 pass-by-value입니다. 참조가 아닌 값에 의해 호출됩니다.
stackoverflow에서 찾은 예제로 설명드리겠습니다.
예제1.
- myCat은 Cat이 아닙니다. Cat에 대한 포인터입니다. 즉, 주소값을 갖고 있습니다.따라서 만들어진 Cat 객체의 주소가 foo 메소드에 넘겨지게 됩니다.
1
2Cat myCat = new Cat("Rover");
foo(myCat);
Cat 객체의 주소를 42라고 가정하겠습니다. 42가 메소드에 넘겨지겠죠?
foo 메소드가 다음과 같다면,1
2
3
4
5public void foo(Cat someCat) {
someCat.setName("Max"); // 1.
someCat = new Cat("Fifi"); // 2.
someCat.setName("Rowlf"); // 3.
}someCat
은 주소값 42를 갖게 됩니다.
- line 1.
someCat
은 42라는 주소에 해당하는 Cat을 가리킵니다.- Max로 이름을 바꿉니다. (
someCat
의 이름은 Max가 됩니다.)
- line 2.
- 이름이 Fifi인 새로운 Cat 객체가 생성됩니다.
- 주소는 74라고 가정하겠습니다.
- line 3.
someCat
은 74라는 주소에 해당하는 Cat을 가리킵니다.- Rowlf로 이름을 바꿉니다. (
someCat
의 이름은 Rowlf가 됩니다.)
이제, 메소드가 끝났습니다. myCat
은 변경되었을까요?
정답은 “변경되지 않았다.” 입니다.myCat
은 여전히 주소값 42를 갖고 있습니다. 하지만 주목해야 할 점은 “이름은 Max로 변경되었다.” 라는 것입니다.
C 언어를 공부하셨던 분들이라면 C와 유사하지만 다르다 라는 것을 느끼셨을 겁니다. C와 마찬가지로 주소값이 전달되지만, C와 다르게 포인터가 가리키는 곳을 변경할 수 없습니다.
위 코드를 다시 살펴보겠습니다.
- 참조형 타입은 메소드로 주소값이 전달됩니다.
메소드가 리턴되고 Max라고 이름이 바뀌었기 때문입니다.1
2
3public void foo(Cat someCat) {
someCat.setName("Max");
} - 포인터가 가리키는 곳을 변경할 수는 없습니다.
변경이 가능했다면myCat
은 Rowlf라는 이름일텐데 Max이기 때문입니다.
여기선 new 연산자를 통해 다른 주소값을 가진 다른 인스턴스가 생성된 것이고 메소드와 같은 라이프 사이클을 갖습니다. 즉, 메소드 리턴과 동시에 사라지게 됩니다.이게 왜 값에 의한 호출인지 아직 명확하지 않아 보입니다.1
2
3
4public void foo(Cat someCat) {
someCat = new Cat("Fifi");
someCat.setName("Rowlf");
}
다음 예시를 보시면 명확하게 이해가 되실 겁니다.
예제2.
1 | public class Main { |
2-1.
1 | Foo f = new Foo("f"); |
위에서 설명했던 대로 f는 주소값을 가지고 타입이 Foo이고 속성이 f인 객체를 가리킵니다.
2-2.
1 | public static void chanageReference(Foo a) |
a는 Foo 타입으로 선언되어 있고 null로 초기화 되어 있습니다.
2-3.
1 | changeReference(f); |
f를 인자로 넘겨주어 a는 f와 동일한 주소값을 갖게 됩니다. 동일한 주소값이니 똑같은 곳을 가리키고 있는 것을 확인할 수 있습니다.
2-4.
1 | Foo b = new Foo("b"); |
b는 주소값을 가지고 타입이 Foo이고 속성이 b인 객체를 가리킵니다.
2-5.
1 | a = b; |
b의 주소값을 a에 할당하게 되어 a는 b가 가리키는 곳을 가리키게 됩니다.
2-6.
1 | modifyReference(Foo c) { } |
f를 인자로 넘겨주어 c는 f의 주소값을 받게 됩니다. 당연히 f가 가리키는 곳을 가리키겠죠?
2-7.
1 | c.setAttribute("c"); |
c가 가리키고 있는 객체의 속성을 c로 변경합니다. c와 f는 동일한 것을 가리키기에 f가 가리키는 객체의 속성은 c가 되는 것입니다.
call by reference 였다면 f의 주소값을 c나 a가 받았겠죠? 그렇다면 위에 a = b가 되었을 때 f도 바뀌었을 것입니다. 하지만 call by value이기 때문에 f, c, a, b 모두 각각 따로 존재하고 값만 전달 받고 있습니다.
Java에서는 모두 값을 전달하는 pass-by-value라는 것을 기억하시면서 이번 글을 마칩니다.
[Reference]