값 전달과 참조의 복사
Java는 메서드 인자를 항상 값으로 전달합니다.
기본 타입은 실제 값이 복사되고 객체를 가리키는 변수는 참조값이 복사됩니다.
참조가 복사되므로 호출된 메서드에서 같은 객체의 상태를 바꿀 수 있지만 호출자의 변수가 다른 객체를 가리키도록 바뀌지는 않습니다.
이 차이를 참조 전달이라고 잘못 부르면 재대입과 상태 변경을 구분하기 어렵습니다.
이 절에서는 변수, 참조값, 객체라는 세 요소를 따로 추적합니다.
기본 타입 인자는 값 자체가 복사된다
메서드 안에서 매개변수를 바꿔도 호출자 지역 변수에는 영향을 주지 않습니다.
두 변수는 같은 숫자로 시작했을 뿐 서로 독립적입니다.
객체 인자는 참조값이 복사된다
호출자 변수와 매개변수가 같은 객체를 가리키므로 매개변수를 통해 필드를 바꾸면 호출 뒤에도 보입니다.
객체가 공유되는 것과 변수 자체가 공유되는 것은 다릅니다.
Java는 언제나 인자의 값을 복사합니다. 객체의 경우 복사되는 값이 객체 자체가 아니라 객체를 가리키는 참조이므로, 호출자와 매개변수가 같은 객체를 통해 상태 변경을 관찰할 수 있습니다.
public class PassByValue {
static final class Box { int value; }
static void change(int number, Box box) {
number = 99;
box.value = 99;
box = new Box();
box.value = 500;
}
public static void main(String[] args) {
int number = 10;
Box box = new Box();
box.value = 10;
change(number, box);
System.out.println("number=" + number);
System.out.println("box=" + box.value);
}
}메서드 안에서 필드를 바꾼 뒤 호출자 쪽 출력도 달라지는지 확인해 보세요. 이는 “참조 전달”이라는 별도 규칙 때문이 아니라, 복사된 두 참조값이 같은 객체를 가리킨 결과입니다.
number=10
box=99기본값 number는 독립 복사이고 box 참조는 같은 객체 상태를 가리키지만 매개변수 재대입은 밖으로 나오지 않습니다.
호출 순간 기본값 또는 참조값이 새 매개변수 변수에 복사됩니다. 호출자 변수와 매개변수는 이때부터 별개의 변수입니다.
객체 인자라면 두 변수의 참조값이 같아 동일한 객체에 도달합니다. 매개변수를 통해 그 객체의 필드를 바꾸면 호출자도 변경된 상태를 관찰합니다.
매개변수에 새 객체를 대입하면 메서드의 복사본만 다른 곳을 가리킵니다. 호출자 변수까지 바꾸려면 새 참조를 반환하고 호출자가 직접 대입해야 합니다.
매개변수 재대입은 호출자 변수를 바꾸지 않는다
메서드 안에서 매개변수에 새 객체를 대입하면 복사된 참조값만 바뀝니다.
호출자의 변수는 여전히 원래 객체를 가리킵니다.
매개변수에 새 객체를 대입해도 호출자 변수는 여전히 이전 객체를 가리킵니다. box = new Box()와 box.value = 10을 나란히 실행해 보면 참조 변수의 재대입과 참조 대상의 변경을 구분할 수 있습니다.
static String renamed(String current) {
current = "new"; // 복사된 참조 변수 재대입
return current;
}String name = "old";
renamed(name);
System.out.println(name); // old
name = renamed(name); // 반환값을 호출자가 명시적으로 대입변경이 필요 없으면 불변 타입을 선호한다
String이나 record처럼 불변 객체는 참조가 공유돼도 상태 변화가 없어 추론하기 쉽습니다.
가변 객체를 받는 메서드는 무엇을 바꾸는지 이름과 문서로 드러냅니다.
반환값으로 새 상태를 명시할 수 있다
매개변수를 재대입해 호출자 변수를 바꾸려 하지 말고 새 객체나 계산 결과를 반환합니다.
호출자가 대입 여부를 직접 결정하게 합니다.
호출자가 새 상태를 선택하게 하려면 메서드가 새 객체를 반환하고 호출부에서 box = update(box)처럼 받도록 설계합니다. 이 방식은 입력을 몰래 바꾸지 않아 변경 지점과 데이터 흐름이 코드에 남습니다.
Java의 인자 전달은 모두 값 전달이며, 객체에서는 복사된 참조가 같은 상태를 가리킬 수 있다는 사실만 추가됩니다.