-
Java - Call by Value & Call By ReferenceLanguage/Java 2022. 2. 21. 13:23
Call by Value & Call by Reference
Call by Value(값에 의한 호출)
void function(int number) { number = 20; } void main() { int number = 10; function(number); printf("%d", number); // 출력되는 값은 10 }
- 함수가 호출될 때, 메모리 공간 안에서는 함수를 위한 별도의 임시공간이 생성되고, 종료 시 해당 공간이 사라진다.
- 해당 호출 방식은 함수 호출 시 전달되는 변수 값을 복사해서 함수 인자로 전달한다. 이때, 복사된 인자는 함수 안에서 지역적으로 사용되기 때문에 local value의 속성을 가진다.
- 따라서, 함수 안에서 인자 값이 변경되더라도, 외부 변수 값은 변경되지 않는다.
Call by Reference(참조에 의한 호출)
void function(int *number) { *number = 20; } void main() { int number = 10; function(&number); printf("%d", number); // 출력되는 값은 20 }
- 함수가 호출될 때, 메모리 공간 안에서는 함수를 위한 별도의 임시 공간이 생성된다.
- 참조에 의한 호출 방식은 함수 호출 시 인자로 전달되는 변수의 레퍼런스(주소)를 전달한다.
- 따라서, 함수 안에서 인자의 값이 변경되면, 인자로 전달된 외부 변수의 값도 변경된다.
Java의 함수 호출 방식
Java 기본형(Primitive Type)의 함수 호출 방식
public class Main { public static void main(String[] args) { int x = 1; int y = 1; plus(x, y); System.out.println("x = " + x + ", " + "y = " + y); // "x = 1, y = 1" 출력 } static void plus(int a, int b) { a = a + 1; b = b + 1; } }
- 위 코드와 같이 두 개의 변수를 인자로 받아 1씩 증가시키는 메서드를 만든 후 실행해 보았다.
- 실행을 하면 확인해볼 수 있듯이 인자로 넘겨준 두 변수들의 값이 변경되지 않고, 그대로 출력하는 것을 알 수 있다.
- 이러한 결과가 나온 이유를 알기 위해서는 Java의 기본형은 JVM의 Stack 영역에 저장된다는 것을 이해해야 한다.
- main 함수가 실행되면서 String[] args 매개변수부터 Stack 영역에 쌓이게 된다.
- 그다음 x, y 변수를 생성하고 plus() 메서드의 인자 값으로 넘겨준다. 이때, 넘어온 인자 값은 x, y 변수가 아닌 변수의 담긴 값 1만 복사해서 넘겨주는 방식이다.
- 그 후, 매개변수 a, b가 Stack 영역에 쌓이게 된다.
- plus() 메서드가 종료되면 위 그림과 같이 a, b는 Stack 영역에서 사라지게 된다.
- 그래서 결국 x, y 변수는 1을 출력하게 된다.
- JVM의 Stack 영역의 동작 과정을 살펴보면서 Java의 기본형은 Call by Value 방식으로 처리되는 것을 확인해보았다.
Java 참조형(Reference Type)의 함수 호출 방식
- Java 참조형의 경우 객체의 "주소 값"을 매개변수로 전달하기 때문에 Call by Reference가 아니냐는 의문을 가질 수 있다. 하지만, 정확하게 말하면 "주소 값"이 아닌, "주소를 가리키는 참조값"을 넘겨주는 방식이다.
- 또한, 주소 값 자체를 "복사 없이" 인자로 전달하는 것이 아닌, 자기 자신이 가지고 있는 값을 "복사해서" 전달한다.
- 결국, 기본형 변수나 참조형 변수 모두 자기 자신이 가지고 있는 값을 복사해서 전달하기 때문에 Call by Value 방식이라고 할 수 있다.
class Sample { int number; public Sample(int number) { this.number = number; } public int getNumber() { return number; } public void setNumber(int number) { this.number = number; } }
- 필드가 하나 있는 Sample 클래스를 선언하였다.
public class Main { public static void main(String[] args) throws IOException { Sample x = new Sample(1); System.out.println("x = " + x); // "x = Main$Sample@279f2327" 출력 System.out.println("x.number = " + x.getNumber()); // "x.number = 1" 출력 change(x); System.out.println("x = " + x); // "x = Main$Sample@279f2327" 출력 System.out.println("x.number = " + x.getNumber()); // "x.number = 1" 출력 } static void change(Sample a) { a = new Sample(2); } }
- Sample 타입의 변수 x를 생성하고, number 필드를 1로 초기화하였다.
- change() 메서드를 이용해서 인자로 넘어온 인스턴스를 새로운 값으로 초기화한 후 각각의 참조값과 필드 값을 출력해 보면, 필드 값이 바뀌지 않고 기존의 값을 유지하는 것을 확인할 수 있다.
- 이런 부분을 보면 Java가 참조형 또한 Call by Value임을 알 수 있다.
- 이러한 결과가 나오는 것을 이해하기 위해서는 Java의 참조형은 JVM의 Heap 영역에 저장된다는 것을 이해해야 한다.
- 먼저 Sample 객체의 인스턴스 x를 생성하면, 참조 변수와 참조값이 Stack 영역에 생성되고 실제 인스턴스 값은 Heap 영역에 생성된다.
- change() 메서드가 실행되면서 2라는 필드 값을 가진 Sample 객체의 새로운 인스턴스를 생성한 후 a에 넣는다.
- 참조 변수 a에는 참조값이 저장되고 실제 인스턴스 값은 Heap 영역에 저장된다.
- 따라서, 메서드가 종료되었을 때 참조 변수 x는 기존에 자신이 가리키고 있던 인스턴스를 계속해서 참조하고 있는 것이다.
메서드 내부에서 참조형의 필드 값을 변경하는 경우
public class Main { public static void main(String[] args) throws IOException { Sample x = new Sample(1); System.out.println("x = " + x); // "x = Main$Sample@279f2327" 출력 System.out.println("x.number = " + x.getNumber()); // "x.number = 1" 출력 change(x); System.out.println("x = " + x); // "x = Main$Sample@279f2327" 출력 System.out.println("x.number = " + x.getNumber()); // "x.number = 2" 출력 } static void change(Sample a) { a.setNumber(2); } }
- 이번에는 메서드 내에서 객체를 새로 생성하는 것이 아닌, 필드 값을 setNumber() 메서드를 이용해서 바꾸어 보았다.
- 그런데, 이 경우에는 기존의 객체가 가지고 있는 필드 값과 다른 필드 값을 출력하는 것을 확인할 수 있었다.
- 어떻게 해서 이러한 결과가 나오는지 그림을 통해서 알아보겠다.
- 먼저, Sample 인스턴스를 생성하면, 참조 변수 x와 참조값 279f2327이 Stack 영역에 들어간다. 그리고 실제 인스턴스 값은 Heap 영역에 저장된다.
- change() 메서드가 실행되면 매개변수로 들어간 참조값 279f2327이 복사되고 참조 변수 a와 참조값 6cd8737이 Stack 영역에 들어가게 된다.
- 결국, 참조값이 같기 때문에 두 참조 변수는 Heap 영역의 같은 값을 바라보고 있는 것이다.
- 따라서, 위 그림과 같이 a의 값을 setNumber() 메서드를 이용해서 변경하면 x도 같은 값을 바라보고 있기 때문에 변경이 되는 것이다.
- 결국, Java는 기본형이든 참조형이든 모두 Call by Value 방식을 사용한다고 알 수 있다.
결론
ㆍ Java는 기본형 변수(Primitive Type)와 참조형 변수(Reference Type)가 있는데 둘 다 Call by Value 방식으로 처리한다.
ㆍ 기본형 변수는 변수가 가지고 있는 값을 복사해서 넘겨주는 방식이고, 참조형 변수의 경우 값의 레퍼런스가 복사돼서 넘겨주는 방식이다.
출처
ㆍ https://gyoogle.dev/blog/computer-language/Java/Call%20by%20value%20&%20Call%20by%20reference.html
ㆍ https://jackjeong.tistory.com/37
ㆍ https://siyoon210.tistory.com/104
728x90'Language > Java' 카테고리의 다른 글
Java - static (0) 2022.03.04 Java - 동일성(identity) & 동등성(equality) (0) 2022.03.03 Java - 캐스팅(Casting) (0) 2022.02.19 Java - 문자열 클래스 (0) 2022.02.19 Java - Boxing & Unboxing (0) 2022.02.19