김영한의 자바 입문 강의 내용을 정리한 글이다.
배열이 필요한 이유
같은 타입의 값을 여러 개 다뤄야 할 때 변수를 일일이 선언하는 방식은 한계가 있다.
학생이 5명이라서 변수도 5개다. 학생이 100명이 되면 int 변수를 100개 선언해야 한다. 출력 코드도 100줄이 된다. 변수 이름이 모두 다르기 때문에 반복문으로 묶을 수도 없다.
이렇게 같은 타입의 변수를 반복해서 선언하고 반복해서 사용하는 문제를 해결하는 것이 바로 배열이다.
배열의 선언과 생성
배열은 같은 타입의 변수를 하나로 묶어서 사용하기 편하게 만든 구조다.
두 단계로 나뉜다는 점이 일반 변수와 다르다.
1단계 — 배열 변수 선언
int[] students;는 "int형 배열을 담을 수 있는 변수를 선언한다"는 뜻이다. 일반 변수와의 차이는 타입 뒤에 []가 붙는다는 점이다. 이 시점에서 배열이 만들어진 것이 아니다. 변수만 만들어졌을 뿐이고, 아직 배열은 존재하지 않는다.
2단계 — 배열 생성
new int[5]는 int형 변수 5개를 연속으로 담을 수 있는 배열을 새로 만든다는 뜻이다. new는 새로 생성한다는 의미이고, int[5]는 int형 5개짜리라는 뜻이다. 배열을 생성하면 내부 값이 타입에 따라 자동으로 초기화된다.
| 타입 | 기본값 |
|---|---|
int, long, short, byte | 0 |
double, float | 0.0 |
boolean | false |
String, 객체 | null |
3단계 — 배열 참조값 보관
배열을 생성하면 JVM은 해당 배열에 접근할 수 있는 참조값(reference), 즉 메모리 주소를 반환한다. 이 참조값이 앞서 선언한 배열 변수 students에 저장된다.
이후 students를 통해 배열에 접근할 때는 변수에 저장된 참조값을 따라가서 실제 배열 데이터에 도달하는 방식으로 동작한다.
System.out.println(students);를 출력하면 [I@4617c264 같은 값이 나온다. [I는 int형 배열을 뜻하고, @ 뒤의 16진수가 참조값(힙의 메모리 주소)이다.
배열과 메모리 구조 — 힙(Heap)
배열이 왜 이렇게 참조값을 사용하는지 이해하려면 JVM의 메모리 구조를 알아야 한다. JVM은 메모리를 크게 스택(Stack)과 힙(Heap)으로 나눠 사용한다.
| 영역 | 저장 대상 | 생명주기 |
|---|---|---|
| 스택(Stack) | 기본형 변수 값, 참조형 변수의 참조값(주소) | 메서드 호출 시 생성, 종료 시 즉시 제거 |
| 힙(Heap) | 배열, 객체(new로 생성된 것들) | GC(가비지 컬렉터)가 수거할 때까지 유지 |
int a = 10;처럼 기본형 변수는 값 자체가 스택에 저장된다. 반면 new int[5]로 만든 배열은 힙에 저장된다.
배열이 힙에 저장되는 이유는 크기가 동적으로 결정될 수 있기 때문이다.
기본형은 컴파일 시점에 크기가 확정된다(int는 항상 4바이트). 그러나 배열은 크기가 실행 시점에 결정될 수 있다. 스택은 컴파일 시점에 크기가 고정되어야 하는 구조이기 때문에, 가변 크기 데이터는 힙에 저장한다.
힙에 저장된 배열은 메서드가 끝나도 바로 사라지지 않는다. 어떤 변수도 이 배열을 참조하지 않을 때 가비지 컬렉터(GC)가 수거한다.
기본형 vs 참조형
배열을 배우면서 자바의 변수 타입이 두 종류로 나뉜다는 것을 알 수 있다.
기본형(Primitive Type)은 int, long, double, boolean 등 값 자체를 직접 저장하는 타입이다. 변수에 실제 데이터가 들어있고, 크기가 컴파일 시점에 고정된다.
참조형(Reference Type)은 int[] students처럼 데이터가 있는 곳의 주소(참조값)를 저장하는 타입이다. 변수에는 실제 데이터가 없고 힙 어딘가를 가리키는 주소만 있다. 배열뿐 아니라 이후에 배울 객체와 클래스도 모두 참조형이다.
| 구분 | 기본형 | 참조형 |
|---|---|---|
| 저장 내용 | 값 자체 | 참조값(메모리 주소) |
| 메모리 위치 | 스택에 값 직접 저장 | 변수(주소)는 스택, 실제 데이터는 힙 |
| 크기 결정 시점 | 컴파일 타임 (고정) | 런타임 (동적 할당 가능) |
| 예시 | int, double, boolean | 배열, String, 객체 |
기본형이 더 단순한 이유는 크기가 고정되어 있기 때문이다. 참조형을 쓰는 이유는 크기를 동적으로 결정할 수 있고, 복잡한 데이터 구조를 만들 수 있기 때문이다. 기본형과 참조형에 대한 내용은 객체를 설명할 때 더 자세히 다룬다.
배열 사용 — 인덱스
배열의 각 공간에는 번호가 붙어 있다. 이 번호를 인덱스(index)라 한다. 인덱스는 항상 0부터 시작한다. new int[5]로 5개짜리 배열을 만들면 인덱스는 0, 1, 2, 3, 4다. 마지막 인덱스는 (배열 크기 - 1)이다.
students[0] = 90;이 실행되는 과정을 풀어 쓰면 다음과 같다.
students변수에서 참조값(x001)을 읽는다.- x001이 가리키는 힙의 배열로 이동한다.
- 인덱스 0에 해당하는 공간에 90을 저장한다.
인덱스 범위 초과
인덱스 허용 범위를 벗어나면 런타임에 예외가 발생한다.
new int[5]로 만든 배열의 유효 인덱스는 0~4다. students[5]는 존재하지 않는 공간을 참조하므로 런타임에 ArrayIndexOutOfBoundsException이 발생한다. 컴파일은 통과하지만 실행 중에 프로그램이 종료된다.
배열과 for문
배열과 반복문을 결합하면 코드를 크게 줄일 수 있다.
students.length는 배열의 크기를 반환하는 읽기 전용 속성이다. new int[5]로 만든 배열이라면 5를 반환한다. i < students.length를 조건으로 쓰면 배열 크기가 바뀌어도 반복문 조건을 수정할 필요가 없다.
배열 초기화 단축 문법
배열을 선언과 동시에 값을 넣는 세 가지 방법이 있다.
방법 3의 {90, 80, 70, 60, 50}은 배열 변수 선언과 한 줄에 함께 쓸 때만 사용 가능하다. 다음은 컴파일 오류가 발생한다.
자바가 내부적으로 {...} 안의 요소 수를 세어 new int[5]를 만들고 값을 채워 넣는다. 동작은 완전히 동일하고 코드만 짧아진다.
향상된 for문 (for-each)
배열을 처음부터 끝까지 순서대로 탐색하는 것이 가장 흔한 패턴이다. 일반 for문으로 작성하면 인덱스 변수, 종료 조건, 인덱스 증가를 모두 작성해야 한다.
이 패턴을 간결하게 만든 것이 향상된 for문(Enhanced For Loop, for-each문)이다.
: 오른쪽에 순회할 배열을 놓고, 왼쪽에 매 반복마다 꺼낸 값을 담을 변수를 선언한다. 인덱스를 따로 관리하지 않아도 되고, 범위 초과 실수도 없다.
향상된 for문을 쓸 수 없는 경우
인덱스 값이 필요한 상황에서는 일반 for문을 써야 한다.
향상된 for문은 인덱스를 내부에 감춰버리기 때문에 꺼낼 방법이 없다.
2차원 배열
1차원 배열이 값들을 일렬로 나열한 것이라면, 2차원 배열은 행(row)과 열(column)로 구성된 표 형태다.
arr[행][열] 순서로 접근한다. 행은 영어로 row(로우), 열은 column(컬럼)이라 한다.
중첩 for문으로 2차원 배열을 순회하는 패턴이다.
2차원 배열의 length
| 표현 | 의미 | 위 예시 값 |
|---|---|---|
arr.length | 행의 수 | 2 |
arr[0].length | 0번 행의 열 수 | 3 |
arr[1].length | 1번 행의 열 수 | 3 |
2차원 배열은 "배열의 배열"이다. arr[0]은 {1, 2, 3} 배열 자체를 가리키는 참조값이다. 따라서 arr.length는 행의 수를, arr[row].length는 해당 행의 열 수를 반환한다. 행마다 열 수가 달라도 arr[row].length를 쓰면 안전하게 순회할 수 있다.
배열 복사
배열은 참조형이기 때문에 = 연산자로 복사하면 실제 데이터가 복사되는 것이 아니라 참조값만 복사된다.
독립된 복사본을 만들려면 별도의 방법을 사용해야 한다.
방법 1 — for문으로 직접 복사
방법 2 — System.arraycopy()
System.arraycopy()는 JVM 내부에서 네이티브 코드로 동작해 for문보다 빠르다. 대용량 배열 복사에 사용한다.
방법 3 — Arrays.copyOf()
Arrays.copyOf()는 내부적으로 System.arraycopy()를 호출한다. 코드가 간결하다.
2차원 배열이나 객체 배열에서 위 방법들은 얕은 복사(shallow copy)를 수행한다. 내부 배열은 여전히 같은 참조를 공유한다. 완전히 독립적인 복사본이 필요하면 각 내부 배열도 별도로 복사해야 한다.
정리
| 개념 | 핵심 |
|---|---|
| 배열 선언 | int[] arr; — 배열을 담을 변수만 만든 것 |
| 배열 생성 | new int[n] — 힙에 n개짜리 공간 생성, 참조값 반환 |
| 자동 초기화 | 숫자는 0, boolean은 false, 객체는 null |
| 인덱스 | 0부터 시작, 마지막은 length - 1 |
length | 배열 크기를 반환하는 읽기 전용 속성 |
| 참조형 | 변수에는 참조값(주소)만 저장, 실제 데이터는 힙 |
| 향상된 for문 | 인덱스 불필요할 때 사용, 인덱스 접근이 필요하면 일반 for문 |
| 배열 복사 | =는 참조 복사, 독립 복사는 System.arraycopy() 또는 Arrays.copyOf() |
'Study > Java' 카테고리의 다른 글
| [Java] 기본형과 참조형 (1) | 2026.04.05 |
|---|---|
| [Java] 메소드(method) (0) | 2026.04.05 |
| [Java] 훈련 (입출력 등) (0) | 2026.04.04 |
| [Java] 스코프, 형변환 (0) | 2026.04.04 |
| [Java] 반복문 (0) | 2026.04.04 |
