1. 배열 타입 String
먼저 String 타입 배열에 대해 간단히 이야기해 보겠습니다.
이미 말했듯이, 배열은 어떤 타입이든 될 수 있습니다. 따라서 String 타입의 배열도 만들 수 있습니다. 만약 “키보드로 10개의 문자열을 입력하고 화면에 역순으로 출력하는” 프로그램을 작성해야 한다면 코드는 대략 다음과 같을 것입니다.
String[] array = new String[10]; // 요소 10개의 배열 객체를 생성합니다
for (int i = 0; i < 10; i++) // 0부터 9까지 반복
{
array[i] = console.nextLine(); // 키보드에서 문자열을 읽어 배열 칸에 저장합니다
}
for (int i = 9; i >= 0; i--) // 9부터 0까지 반복
{
System.out.println(array[i]); // 배열의 해당 칸을 화면에 출력합니다
}
코드는 거의 달라지지 않았습니다! 배열을 생성할 때 타입 int를 String으로 바꾸기만 하면 됩니다. 그 외에는 완전히 동일합니다!
null 소개
자, 퀴즈입니다 — 이 배열들을 막 생성한 직후 각 칸에는 무엇이 들어 있을까요?
int[] numbers = new int[10];
String[] strings = new String[10];
User[] users = new User[10];
생각해 보면, 아마도 numbers의 칸에는 0이 들어 있겠죠. 맞습니다.
그렇다면 같은 논리로 strings의 칸에는 빈 문자열 — ""이 들어 있어야 할까요? 그럴듯해 보입니다.
그럼 users 배열의 칸에는 무엇이 들어 있을까요? 빈 User 객체들?
바로 여기에 함정이 있습니다. 모든 타입에 “기본값”이 있는 것은 아닙니다. 그래서 Java 설계자들은 특별한 상수 — null을 도입했습니다. null은 빈 참조입니다. 객체 타입의 변수가 생성되면 그 시작 값은 null입니다. 아무 것도 가리키지 않는, 참조의 부재죠.
String name; // name에는 null이 들어 있다
name = "Alex"; // name에는 "Alex" 문자열/객체에 대한 참조가 들어간다
name = null; // name에는 null이 들어 있다
null 다루기
참조가 null인 경우에는 해당 객체의 메서드를 호출할 수 없습니다 — 객체가 없기 때문이죠! 아래 코드에서는 프로그램이 오류와 함께 종료됩니다:
String name; // name에는 null이 들어 있다
String upperName = name.toUpperCase(); // NullPointerException 발생: name == null
아까 그 ‘퀴즈’의 정답은 다음과 같습니다:
int[] numbers = new int[10]; // {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
String[] strings = new String[10]; // {null, null, null, null, null, null, null, null, null, null}
User[] users = new User[10]; // {null, null, null, null, null, null, null, null, null, null}
기본값이 있는 것은 오직 기본형뿐입니다. 나머지 타입의 기본값은 null입니다. String의 시작 값은 빈 문자열이 아닙니다. 그런 법이죠.
2. 메모리에서의 String 타입 배열
그림 1. String 객체가 메모리에 배치되는 방식:

문자열의 텍스트는 변수 안에 직접 저장되지 않습니다. 별도의 메모리 블록이 할당되고, String 타입 변수에는 그 텍스트 객체의 주소(참조)가 저장됩니다.
그림 2. 정수 배열이 메모리에 배치되는 방식:

이 그림은 이미 본 적이 있을 겁니다.
그림 3. 문자열 배열이 메모리에 배치되는 방식:

왼쪽에는 String[] 타입의 배열 변수(배열 객체의 주소를 저장)가 있습니다.
가운데에는 String 타입의 배열 객체가 있습니다.
오른쪽에는 어떤 텍스트를 담고 있는 문자열 객체들이 있습니다.
String 타입의 배열 객체 칸에는 문자열(텍스트) 자체가 아니라 그 주소(참조)가 저장됩니다. 이는 String 타입 변수에 문자열(텍스트)의 주소가 저장되는 것과 정확히 같습니다.
3. Java에서 배열 빠른 초기화
배열은 매우 유용하기 때문에 Java 개발자들은 배열 작업을 최대한 편리하게 만들고자 했습니다. 그들이 가장 먼저 한 일은 배열 초기화, 즉 시작 값을 넣는 작업을 단순화하는 것이었습니다.
프로그램이 어디선가 읽어들이는 데이터 외에도, 동작을 위해 자체적인 내부 데이터가 필요한 경우가 자주 있습니다. 예를 들어, 각 달의 일수를 배열에 저장해야 한다고 해 봅시다. 코드는 다음과 같을 수 있습니다:
int[] months = new int[12];
months[0] = 31; // 1월
months[1] = 28; // 2월
months[2] = 31; // 3월
months[3] = 30; // 4월
months[4] = 31; // 5월
months[5] = 30; // 6월
months[6] = 31; // 7월
months[7] = 31; // 8월
months[8] = 30; // 9월
months[9] = 31; // 10월
months[10] = 30; // 11월
months[11] = 31; // 12월
하지만 더 짧게 쓸 수 있는 방법이 있습니다 — Java 설계자들에게 감사하죠:
// 각 달의 일수
int[] months = new int[] { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
그냥 배열의 모든 값을 쉼표로 나열하면 됩니다!
컴파일러는 변수-배열의 타입을 보고 컨테이너(배열 객체)의 타입을 추론할 수 있습니다. 그리고 배열 길이는 중괄호 안에 쓴 요소의 개수를 세어 결정합니다.
그래서 이 코드는 더 짧게 쓸 수 있습니다:
// 각 달의 일수
int[] months = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
정말 멋지지 않나요? 🙂 이런 표기를 “배열의 빠른 초기화”라고 합니다. 참고로 int 타입에만 동작하는 것은 아닙니다...
// 월 이름
String[] months = { "1월", "2월", "3월", "4월", "5월", "6월", "7월", "8월", "9월", "10월", "11월", "12월" };
4. 루프 for-each
배열의 모든 요소를 순회하는 작업은 너무나 자주 나오기 때문에 이를 위한 전용 루프 — for-each가 있습니다. 표기는 다음과 같습니다:
for (int score : scores)
{
System.out.println("점수: " + score);
}
여기서는 루프가 요소를 직접 순회합니다 — 수동 인덱스가 없습니다. 다만 요소를 직접 변경할 수는 없습니다(루프 변수에는 값의 복사본만 들어 있기 때문). 대신 배열을 단순히 “훑어보기”에는 매우 편리합니다.
컴파일러는 for-each 루프 코드를 아래 코드로 바꿉니다:
for (int i = 0; i < scores.length; i++)
{
int score = scores[i];
System.out.println("점수: " + score);
}
그러니 마법 같은 건 없습니다. 이제 왜 score 변수를 통해 배열 요소를 변경할 수 없는지 이해될 것입니다.
예제를 하나 더 써 봅시다: 모든 점수의 합을 구해 보겠습니다.
int sum = 0;
for (int score : scores)
{
sum += score;
}
System.out.println("모든 점수의 합: " + sum);
어떤 반복문을 언제 사용할까?
- 인덱스가 필요하거나 요소를 변경해야 한다면 — for 루프.
- 값들을 그냥 순회하기만 하면 된다면 — for-each가 더 빠르고 깔끔합니다.
5. 배열 요소 변경
만약 각 점수를 1점씩 올려야 한다면(예: 모범 행동 보너스) 어떻게 할까요? 이때는 인덱스가 있는 고전적인 for를 사용해야 합니다. 그래야 배열의 값을 변경할 수 있습니다.
예: 각 요소를 1씩 증가
for (int i = 0; i < grades.length; i++) {
grades[i] = grades[i] + 1;
}
이 코드를 실행한 뒤에는 grades 배열에 새로운 값이 들어갑니다.
왜 for-each로는 이렇게 할 수 없을까?
for (int grade : grades) {
grade = grade + 1; // 동작하지 않습니다!
}
이 코드는 배열을 변경하지 못합니다. 왜냐하면 grade는 배열 값의 복사본일 뿐, 요소 자체에 대한 참조가 아니기 때문입니다.
6. 배열을 이용한 계산
배열은 종종 계산에 사용됩니다: 합계, 평균, 최댓값, 최솟값 등.
예: 배열의 모든 요소 합
int sum = 0;
for (int i = 0; i < grades.length; i++) {
sum += grades[i]; // sum = sum + grades[i]; 와 동일
}
System.out.println("점수 합계: " + sum);
예: 최댓값 찾기
int max = grades[0]; // 첫 요소부터 시작
for (int i = 1; i < grades.length; i++) {
if (grades[i] > max) {
max = grades[i];
}
}
System.out.println("최고 점수: " + max);
예: 최솟값 찾기
int min = grades[0]; // 첫 요소부터 시작
for (int i = 1; i < grades.length; i++) {
if (grades[i] < min) {
min = grades[i];
}
}
System.out.println("최저 점수: " + min);
예: 평균값 계산
int sum = 0;
for (int i = 0; i < grades.length; i++) {
sum += grades[i];
}
double average = (double) sum / grades.length; // 반드시 double로 형변환!
System.out.println("평균 점수: " + average);
주의: 정수가 아닌 실수를 얻으려면, 나누기 전에 합계를 double로 명시적으로 변환해야 합니다.
7. 실전 과제
키보드로 배열 입력
사용자가 입력한 값으로 배열을 채워야 할 때가 자주 있습니다. 이를 위해 Scanner와 루프를 사용합니다.
Scanner console = new Scanner(System.in);
int n = 5; // 배열 크기
int[] numbers = new int[n];
System.out.println("정수 " + n + "개를 입력하세요:");
for (int i = 0; i < n; i++) {
numbers[i] = console.nextInt();
}
System.out.println("입력한 값:");
for (int i = 0; i < n; i++) {
System.out.println(numbers[i]);
}
배열을 역순으로 출력
때로는 배열의 끝에서부터 요소를 출력해야 할 때가 있습니다(예: 누가 마지막으로 방에 들어왔는지 확인).
for (int i = grades.length - 1; i >= 0; i--) {
System.out.println("점수 No. " + (i + 1) + ": " + grades[i]);
}
8. 1차원 배열 작업 시 흔한 실수
오류 №1: 배열 범위를 벗어남
가장 흔한 문제는 존재하지 않는 요소에 접근하려는 시도입니다. Java에서는 ArrayIndexOutOfBoundsException 예외가 발생합니다. 기억하세요: 마지막으로 허용되는 인덱스는 arr.length - 1입니다.
int[] arr = new int[5];
System.out.println(arr[5]); // 오류! 인덱스는 0부터 4까지.
오류 №2: 배열 초기화를 잊음
변수만 선언하고, 배열을 생성하지 않았습니다:
int[] arr;
arr[0] = 5; // 오류! 배열이 생성되지 않았습니다.
new를 사용해 반드시 배열을 생성해야 합니다:
arr = new int[10];
오류 №3: for-each로 배열 요소를 변경하려 함
for-each 루프의 변수는 요소에 대한 참조가 아니라 값의 복사본입니다:
for (int x : arr) {
x = 100; // 배열이 바뀌지 않습니다!
}
값을 변경하려면 인덱스가 있는 일반 for를 사용하세요.
오류 №4: 배열 길이를 잘못 사용
가끔 배열의 길이와 마지막 인덱스를 혼동합니다:
for (int i = 0; i <= arr.length; i++) { // 오류! i < arr.length 이어야 합니다
// ...
}
이 코드는 범위를 벗어나게 됩니다.
오류 №5: 암시적 형 변환
int 타입 배열에는 double 값을 직접 대입할 수 없습니다:
int[] arr = new int[3];
arr[0] = 3.14; // 오류! 3.14는 double입니다.
GO TO FULL VERSION