1. 소개
여러 프로그래밍 언어에는 함수(또는 메서드)에 매개변수를 전달하는 두 가지 기본 방식이 있습니다:
- 값에 의한 전달: 함수는 변수 값의 복사본을 받습니다. 함수가 매개변수를 변경해도 원본은 바뀌지 않습니다.
- 참조에 의한 전달: 함수는 원본 객체에 대한 “참조”(포인터)를 받습니다. 함수 내부의 변경이 원본에 반영됩니다.
Java에서는 모든 매개변수가 값에 의해서만 전달됩니다!
하지만 여기엔 함정이 있습니다. 원시 타입(int, double, boolean 등)을 전달할 때는 값 자체가 복사됩니다. 반면 참조 타입(예: 배열이나 객체)을 전달할 때는 참조의 값(즉, 메모리에서 객체의 “주소”)이 복사될 뿐, 객체 자체는 복사되지 않습니다.
머리가 복잡해지기 시작해도 걱정하지 마세요 — 지금부터 차근차근 정리하고 예제로 보여 드리겠습니다!
2. 원시 타입: 값이 복사됩니다
간단한 것부터 시작해 봅시다. 메서드 안에서 int 타입 변수의 값을 바꾸려고 하면 무슨 일이 일어날까요?
public class Demo
{
public static void main(String[] args)
{
int number = 5;
changeValue(number);
System.out.println(number); // 무엇이 출력될까요?
}
public static void changeValue(int n)
{
n = 42;
}
}
결과: 화면에 5가 출력됩니다.
왜 그런가요?
메서드 changeValue에는 변수 number의 값 복사본이 전달됩니다. 메서드 내부에서는 복사본(n)만 바뀌며, main의 원본 변수 number는 그대로입니다.
비유
비유하자면, 원본이 아니라 여권의 복사본을 친구에게 건넨 것과 같습니다. 친구가 복사본에 수염을 얼마든지 그려도 — 당신의 진짜 여권은 그대로입니다!
3. 참조 타입: 참조가 복사됩니다
이제 배열로 같은 실험을 해 보겠습니다:
public class Demo
{
public static void main(String[] args)
{
int[] numbers = {1, 2, 3};
changeFirst(numbers);
System.out.println(numbers[0]); // 무엇이 출력될까요?
}
public static void changeFirst(int[] arr)
{
arr[0] = 99;
}
}
결과: 화면에 99가 출력됩니다.
왜 그런가요?
메서드 changeFirst에는 배열 numbers에 대한 참조의 복사본이 전달됩니다. 두 이름(main의 numbers와 메서드의 arr)은 메모리에서 같은 배열을 가리킵니다. 따라서 메서드 안에서 배열의 요소를 바꾸면, 그 변경이 바깥에서도 보입니다.
시각화
main: numbers ──► [1, 2, 3]
▲
│
changeFirst: arr ──┘
arr[0]을 변경하면 실제로 numbers[0]이 바뀝니다. 둘 다 동일한 객체를 가리키기 때문입니다.
4. 하지만 참조 자체를 바꾸면 — 원래 객체는 변하지 않습니다!
이번엔 이렇게 해 봅시다:
public class Demo
{
public static void main(String[] args)
{
int[] numbers = {1, 2, 3};
replaceArray(numbers);
System.out.println(numbers[0]); // 무엇이 출력될까요?
}
public static void replaceArray(int[] arr)
{
arr = new int[] {10, 20, 30};
}
}
결과: 1을 출력합니다.
왜 그런가요?
메서드 replaceArray에서 변수 arr는 새로운 배열을 가리키게 되지만, 이는 main의 변수 numbers에는 아무 영향도 주지 않습니다.
두 변수는 같은 참조를 공유하고 있었지만, 메서드 내부에서 arr = ...를 수행하면 그것은 오직 로컬 변경일 뿐입니다.
비유
이는 당신과 친구가 같은 집 열쇠를 하나씩 갖고 있던 상황과 같습니다. 친구가 새 열쇠를 만들어도 당신의 예전 열쇠는 그대로이고, 문도 그대로입니다.
5. 개념 확인: 메서드로 두 int를 “서로 바꾸기”는 불가능
많은 초보자가 두 수를 서로 바꾸는 메서드를 작성하려고 하지만, 제대로 작동하지 않습니다:
public class Demo
{
public static void main(String[] args)
{
int a = 5, b = 10;
swap(a, b);
System.out.println(a + " " + b); // 예상: 10 5, 실제로는?
}
public static void swap(int x, int y)
{
int temp = x;
x = y;
y = temp;
}
}
결과: 화면에 5 10이 출력됩니다.
왜일까요?
swap에는 변수 a와 b의 복사본이 전달됩니다. swap 내부에서 하는 모든 작업은 이 복사본들에만 영향을 주며, 원본에는 영향을 주지 않습니다.
6. 하지만 배열은 내용물을 바꿀 수 있습니다!
배열에서 값을 서로 바꾸는 올바른 방법을 보겠습니다:
public class Demo
{
public static void main(String[] args)
{
int[] arr = {5, 10};
swap(arr);
System.out.println(arr[0] + " " + arr[1]); // 예상: 10 5
}
public static void swap(int[] arr)
{
int temp = arr[0];
arr[0] = arr[1];
arr[1] = temp;
}
}
결과: 10 5를 출력합니다.
왜 그런가요?
원본 변수와 메서드 매개변수 모두가 가리키는 객체(배열)의 내용물을 변경하기 때문입니다.
7. 데모: 객체와 그 필드
배열은 이해했습니다. 그럼 우리가 정의한 클래스의 객체는 어떨까요? 완전히 동일합니다!
class Box
{
int value;
}
public class Demo
{
public static void main(String[] args)
{
Box box = new Box();
box.value = 7;
changeBox(box);
System.out.println(box.value); // 무엇이 출력될까요?
}
public static void changeBox(Box b)
{
b.value = 42;
}
}
결과: 42를 출력합니다.
왜 그런가요?
참조가 가리키는 객체의 필드를 변경하고 있기 때문입니다.
메서드로 매개변수를 전달할 때 무슨 일이 일어나는가
| 무엇을 전달하는가 | 어떻게 복사되는가 | 메서드 안에서 변경 가능? | 바깥에서 변경이 보이나? |
|---|---|---|---|
| int, double 등 | 값의 복사본 | 가능, 하지만 복사본만 | 아니오 |
| 배열 | 참조의 복사본 | 가능, 요소를 변경하는 경우 | 예 |
| 객체(클래스) | 참조의 복사본 | 가능, 필드를 변경하는 경우 | 예 |
| 배열/객체 | 참조의 복사본 | 불가, 새 참조를 할당하는 경우 | 아니오 |
8. 흔한 실수
오류 № 1: 메서드 호출 후 int가 바뀔 것이라고 기대함. 많은 초보자가 int 변수를 메서드에 넘기면 메서드 내부에서 그 값을 바꿀 수 있다고 생각합니다. 이는 잘못입니다. 바뀌는 것은 오직 복사본뿐입니다!
오류 № 2: 메서드로 두 int를 서로 바꾸려 함. 이미 보았듯이 이는 불가능합니다 — 서로 바뀌는 것은 변수의 복사본뿐이며, 원본은 영향을 받지 않습니다.
오류 № 3: 메서드로 배열을 “교체”하려 함. 메서드 안에서 매개변수-배열에 새 배열(arr = new int[]{...})을 할당해도 원본 배열에는 영향을 주지 않습니다. 배열을 “교체”하려면 — 메서드에서 새 배열을 반환하고 호출하는 코드의 변수에 그것을 할당해야 합니다.
오류 № 4: 객체의 내용물을 바꿀 수 있다는 사실을 잊음. 객체(또는 배열)를 전달한다면, 그 필드/요소의 변경은 바깥에서도 보인다는 점을 잊지 마세요!
오류 № 5: null과의 혼동. null인 참조를 메서드에 전달하고 그 필드에 접근하려고 하면 NullPointerException이 발생합니다. 주의하세요!
GO TO FULL VERSION