-
Java - 메서드 호출 시 파라미터를 전달하는 방법(Call By Value, Call By Reference)개발언어/JAVA 2023. 10. 10. 18:12
Java에서 메서드 호출시 파라미터를 전달하는 방법
메서드를 호출할 때 파라미터를 전달하는 방법에는 두 가지가 있다. 하지만 자바는 오로지 Call By Value로 만 작동한다.
1. JVM 메모리에 변수가 저장되는 위치
Java의 call by value에 대해 이해하기 위해서는 먼저 변수 생성 시 메모리에 어떤 식으로 저장되는지 알아야한다. Java에서 변수를 선언하면 Stack 영역에 할당된다. 원시 타입(Primitive Type)은 Stack 영역에 변수와 함께 저장된다. 참조 타입(Reference Type)객체는 Heap 영역에 저장되고 Stack영역에 이쓴 변수가 객체의 주소값을 갖고 있다.
2.원시 타입(Primitive Type)전달
public class PrimitiveTypeTest{ @Test @DisplayName("Primitive Type은 Stack 메모리에 저장되어서 변경해도 원본 변수에 영향이 없다.") void test(){ int a = 1; int b = 2; //Before assertEquals(a,1); assertEquals(b,2); modify(a,b); //After: modify(a,b) 호출 후에도 값이 변하지 않음 assertEquals(a,1); assertEquals(b,2); } private void modify(int a, int b){ // 여기 있는 파라미터 a,b는 이름만 같을 뿐 test()에 있는 a,b와 다른 변수 a = 5; b = 10; } }
위 코드에서 test()의 변수 a,b와 modify(a,b)로 전달받은 파라미터 a,b의 이름과 값은 같다. 하지만 test()의 a,b와 modify() ab는 다르다. modify(a,b)를 호출하는 순간 Stack 영역에 새로운 변수 a,b가 새로 생성되어 총 4개의 변수가 존재하게된다.
Stack 내부에 test()와 modify()라는 영역이 나누어져 있고 거기에 동일한 이름을 가진 변수 a,b가 존재한다. 즉 modify()영역의 값을 바꿔도 test()영역의 변수는 변화가 없다. 원시 타입의 전달은 값만 전달하는 call by value로 동작한다.
3-1.참조 타입(Reference Type)전달
class User{ public int age; public User(int age){ this.age = age; } } public class ReferenceTypeTest{ @Test @DisplayName("Reference Type은 주소값을 넘겨 받아서 같은 객에를 바라본다."+ "그래서 변경하면 원본 변수에도 영향이 있다.") void test(){ User a = new User(10); User b = new User(20); // Before assertEquals(a.age,10); // true assertEquals(b.age,20); // true modify(a,b); // After assertEqual(a.age,11); //true assertEquals(b.age,20); //true } private void modify(User a, User b){ //a,b와 이름이 같고 같은 객체를 바라본다. //하지만 test에 있는 변수와 확실히 다른 변수다. //modify의 a와 test의 a 는 같은 객체를 바라봐서 영향이있다. a.age++; // b에 새로운 객체를 할당하면 가리키는 객체가 달라지고 원본에는 영향 없음 b = new User(30); b.age++; } }
원시 타입 코드와 마찬가지로 동일한 변수 a,b가 존재한다. 여기서 modify(a,b)를 호출 후에 a.age의 값이 변경되었기 때문에 call by reference로 파라미터를 넘겨주었다고 착각하기 쉽다. 하지만 reference 자체를 전달하는게 아니라 주소값만 전달해주고 modify()에서 생긴 변수들이 주소값을 보고 같은 객체를 참고하고 있는 것이다.
test() 영역과 modify() 영역에 존재하는 a라는 변수들은 heap영역에 존재하는 같은 객체인 User01을 바라보고 있기 때문에 객체를 공유한다. 반면 b라는 변수는 modify메서드 실행 직 후에는 heap영역에 존재하는 같은 객체인 User02를 바라보고 있었으나 modify()메서드 작동과정에서 새로운 객체를 생성해서 할당했기 때문에 User03이라는 객체를 바라보게된다. 그래서 User03의 age값을 변경해도 test()에 있는 b에는 아무런 변화가 없다.
3-2.test()메서드가 끝난 후 최종 메모리 상태
modify(a,b)메서드를 빠져나오면서 modify stack 영역에 할당된 변수들은 사라진다. 최종적으로 위 그림과 같은 상태가 되며 User03은 어떤 곳에서도 참조되고 있지 않기 때문에 나중에 Garbage Collector에 의해 제거될 것이다.
Call By Value
call by value는 메서드를 호출할 때 값을 넘겨주기 때문에 pass by value라고도 부른다. 메서드를 호출하는 호출자(Caller)의 변수와 호출 당하는 수신자(Callee)의 파라미터는 복사된 서로 다른 변수 이다. 값만을 전달하기 때문에 수신자의 파라미터를 수정해도 호출자의 변수에는 아무런 영향이 없다.
Call By Reference
call by reference는 참조(heap영역의 주소)를 직접 전달하며 pass by reference라고도 부른다. 참조를 직접 넘기기 때문에 호출자는 변수와 수신자의 파라미터는 완전히 동일한 변수이다. 메서드 내에서 파라미터를 수정하면 그대로 원본 변수에도 반영된다.
정리
결국 주소값을 넘기는 것이 Call By Reference라고 생각할 수도 있지만, 진짜 Call By Reference의 경우는 참조 자체를 넘기기 때문에 새로운 객체를 할당하면 원본 변수도 영향을 받는다. 가장 큰 핵심은 호출자 변수와 수신자 파라미터는 비록 동일한 heap영역에서는 reference를 참조할지라도 stack 영역에서는 각각 존재하는 다른 변수라는 것이다.
'개발언어 > JAVA' 카테고리의 다른 글
Java - Object4 (1) 2023.10.17 Java - Object3 (1) 2023.10.16 Java - Object 1 (0) 2023.10.16 JAVA문법 IndexOf(),Substring() (0) 2022.01.14 char타입 다루기 (0) 2022.01.13