Java 객체 배열과 참조 교환
선수학습(1개)
요약
Java에서 객체 배열이 참조를 저장하는 방식과, 배열 요소를 교환할 때 원본 변수에 어떤 영향을 미치는지 알아봅니다. static nested class 개념과 함께 정보처리기사 실기 문제를 풀어봅니다.
객체 배열 핵심 정리
| 개념 | 설명 | 예시 |
|---|---|---|
| 객체 배열 | 배열의 각 요소가 객체의 참조(주소) 를 저장 | BO[] arr = {a, b, c}; |
| 빈 객체 배열 | 배열만 만들면 각 칸은 null, 객체는 따로 만들어 넣어야 함 | BO[] arr = new BO[3]; |
| 참조 교환 | 배열 요소를 교환하면 참조만 바뀜, 원본 변수는 그대로 | arr[0] = arr[2]; |
| 필드 수정 | 배열 요소를 통해 객체 필드를 수정하면 원본 객체가 변경됨 | arr[1].v = 10; |
| static nested class | 외부 클래스의 인스턴스 없이 독립적으로 사용 가능한 내부 클래스 | public static class BO |
먼저 알아야 할 것: 변수에 저장되는 것 쌩기초
객체 배열을 이해하려면, Java에서 변수에 값을 대입(=)할 때 무엇이 저장되는지 알아야 합니다.
| 종류 | 변수에 저장되는 것 | 대입 시 |
|---|---|---|
기본형 (int, double 등) | 값 자체 | 값이 복사됨 |
| 참조형 (객체, 배열) | 객체의 주소 | 주소가 복사됨 |
a에는 BO 객체가 통째로 들어있는 것이 아닙니다. a에는 BO 객체가 있는 주소가 저장되어 있습니다. 이것이 이 페이지의 모든 내용을 이해하는 핵심입니다.
자세한 설명은 기본형 vs 참조형 페이지를 참고하세요.
객체 배열이란? 쌩기초
객체 배열 은 객체를 여러 개 담을 수 있는 배열입니다.
BO[] arr = {a, b, c}에서 일어나는 일을 정확히 말하면: a의 값이 arr[0]에 복사됩니다. 그런데 a는 참조형이므로 a의 값은 객체의 주소입니다. 결국 arr[0]에는 a와 같은 주소가 들어가고, 둘 다 같은 BO 객체를 가리키게 됩니다.
a와arr[0]→ 같은 주소 → 같은 BO 객체(v=1)b와arr[1]→ 같은 주소 → 같은 BO 객체(v=2)c와arr[2]→ 같은 주소 → 같은 BO 객체(v=3)

배열을 만들면 객체도 만들어질까? 쌩기초
앞의 예시는 BO 객체를 먼저 만들어 두고(new BO(1)) 배열에 담았습니다. 그런데 객체 없이 배열만 먼저 만들 수도 있습니다.
이때 만들어지는 것은 BO 객체 3개가 아닙니다. BO 객체의 주소를 담을 빈 칸 3개만 생깁니다. 아직 new BO(...)를 한 번도 하지 않았으니 담을 주소가 없고, 세 칸에는 담고 있는 주소가 없다는 뜻의 null이 들어 있습니다.
| 코드 | 만들어지는 것 |
|---|---|
new BO[3] | BO 객체의 주소를 담을 빈 칸 3개 (객체는 0개) |
new BO(3) | v가 3인 BO 객체 1개 |
대괄호 [3]은 "칸을 3개 만들어라", 소괄호 (3)은 "v가 3인 객체를 만들어라"입니다. 같은 new지만 괄호 모양에 따라 만들어지는 것이 완전히 다릅니다.
| 인덱스 | 담고 있는 주소 |
|---|---|
| arr[0] | null |
| arr[1] | null |
| arr[2] | null |
int[] nums = new int[3]처럼 기본형 배열은 각 칸이 0으로 채워지지만, 객체 배열의 칸에는 null이 들어갑니다. 기본형 vs 참조형의 차이가 배열에서도 그대로 나타나는 것입니다.
그래서 이 상태에서 곧바로 필드에 접근하면 실행이 중단됩니다.1
칸을 먼저 채워야 합니다.
배열 요소 교환 (참조 교환) 기초
다시 앞의 BO[] arr = {a, b, c}; 상태로 돌아가 보겠습니다.
배열의 요소를 교환하면, 배열 안의 주소만 바뀝니다. 원본 변수 a, b, c에 저장된 주소는 변하지 않습니다.
여기서도 같은 원리입니다. BO t = arr[0]은 arr[0]에 저장된 값을 t에 복사합니다. arr[0]의 값은 객체의 주소이므로, t에도 같은 주소가 들어가고 같은 객체를 가리키게 됩니다.
교환 전후 비교
| arr[0] | arr[1] | arr[2] | |
|---|---|---|---|
| 교환 전 | a의 객체(v=1)의 주소 | b의 객체(v=2)의 주소 | c의 객체(v=3)의 주소 |
| 교환 후 | c의 객체(v=3)의 주소 | b의 객체(v=2)의 주소 | a의 객체(v=1)의 주소 |
교환된 것은 배열 안에 저장된 주소뿐입니다. 변수 a에 저장된 주소는 그대로이므로, a는 여전히 v=1인 객체를 가리킵니다. c도 마찬가지로 여전히 v=3인 객체를 가리킵니다.
배열을 통한 필드 수정 기초
배열 요소를 통해 객체의 필드를 수정하면, 원본 객체가 실제로 변경됩니다. arr[1]과 b에 같은 주소가 들어있으므로, 어느 쪽을 통해 수정하든 같은 객체가 바뀝니다.
교환 후 arr[1]에는 b의 객체 주소가, arr[0]에는 c의 객체 주소가 들어있습니다. 따라서 위 코드는 다음과 같습니다.
b.v가 3으로 변경됩니다. arr[1]과 b는 같은 주소를 가지고 있으므로, arr[1]을 통해 바꾼 것이 b를 통해서도 보입니다. 이것은 Call by Value에서 배열 요소 수정과 같은 원리입니다.
static nested class 기초
이 문제에서 BO 클래스는 Main 클래스 안에 static으로 선언되어 있습니다.
static nested class 는 외부 클래스의 인스턴스 없이 독립적으로 사용할 수 있는 내부 클래스입니다. 일반 클래스와 거의 동일하게 동작합니다.
| 특징 | 설명 |
|---|---|
| 독립적 생성 | new Main()없이 new BO(1)로 직접 생성 가능 |
| 외부 클래스 접근 | 외부 클래스(Main)의 인스턴스 멤버에 직접 접근 불가 |
| 같은 파일 안에 선언 | main 메서드에서 바로 사용할 수 있음 |
정리 심화
| 동작 | 설명 | 원본 영향 |
|---|---|---|
arr[0] = arr[2] | 배열 안의 참조를 교환 | 없음 (a, b, c 변화 없음) |
arr[1].v = 10 | 배열 요소가 가리키는 객체의 필드 수정 | 있음 (원본 객체 변경) |
정보처리기사 실기 기출 문제
Footnotes
-
이때 발생하는 오류의 이름이 NullPointerException입니다. 직접 코드를 실행해보면 이 이름이 보입니다. ↩