
기본형마다 짝이 되는 포장 클래스가 있습니다. 포장하고 푸는 일은 자바가 자동으로 합니다.
지난 편에서 넘어간 숙제가 하나 있습니다. 컬렉션의 꺾쇠에는 int를 못 쓰고 Integer를 써야 한다고 했죠. 그런데 이 Integer는 대체 무엇이고, int와 뭐가 다를까요. 오늘 앞부분에서 이 정체를 밝힙니다. 그리고 뒷부분에서는 모든 프로그램이 결국 만나게 되는 주제, 날짜와 시간을 다룹니다. 시험까지 며칠 남았는지 계산하는 코드를 오늘 직접 돌려 봅니다.
Integer의 정체 — 래퍼 클래스
Integer는 int 값 하나를 클래스로 감싼 것입니다. 이런 클래스를 래퍼 클래스(Wrapper class)라고 부릅니다. 랩(wrap), 즉 포장한다는 뜻 그대로입니다.
쉽게 — 알사탕(기본형 값)과 포장지에 싼 사탕(래퍼 객체)의 관계입니다. 내용물은 같지만, 포장이 있어야 들어갈 수 있는 자리가 있습니다. 선물 상자(컬렉션)에는 포장된 사탕만 담는 것이죠.
왜 이런 게 필요할까요. 자바의 기본형 8개는 클래스가 아닙니다. 그런데 제네릭과 컬렉션의 타입 자리에는 클래스만 올 수 있습니다. 그래서 자바는 기본형마다 짝이 되는 포장 클래스를 만들어 뒀습니다. int는 Integer, double은 Double, char는 Character, boolean은 Boolean. 나머지도 규칙은 같습니다. 대부분 첫 글자만 대문자로 바뀐 이름이고, int와 char만 풀네임이 됩니다.
포장과 풀기는 자동입니다 — 오토박싱
포장을 씌우는 것을 박싱(boxing), 벗기는 것을 언박싱(unboxing)이라고 합니다. 다행히 요즘 자바에서는 이 일을 컴파일러가 알아서 해줍니다.
Integer a = 10; // int → Integer 자동 포장 (오토박싱)
int b = a + 5; // Integer → int 자동 풀기 (오토언박싱)
ArrayList<Integer> nums = new ArrayList<>();
nums.add(3); // int를 넣었지만 자동으로 Integer가 되어 들어감
int first = nums.get(0); // 꺼낼 때도 자동으로 풀림
지난 편 컬렉션 예제에서 숫자를 그냥 넣고 꺼냈던 것이 바로 오토박싱 덕분이었습니다. 문법이 편해진 만큼 정체를 몰라도 코드는 돌아가지만, 잠시 뒤에 보듯 모른 채 지나가면 당하는 함정이 하나 있습니다.
문자열을 숫자로 — 래퍼의 두 번째 직업
래퍼 클래스는 포장지 역할 말고도 변환 도구 역할을 합니다. 가장 많이 쓰는 것이 문자열을 숫자로 바꾸는 parseInt입니다.
String input = "85";
int score = Integer.parseInt(input); // "85" → 85
double d = Double.parseDouble("3.14"); // "3.14" → 3.14
String s = String.valueOf(100); // 100 → "100" (반대 방향)
화면이나 파일에서 읽은 값은 전부 문자열로 들어옵니다. "85" + 15를 계산하면 100이 아니라 "8515"가 되죠. JAVA1에서 배운 문자열 연결입니다. 그래서 계산하려면 먼저 숫자로 바꾸는 이 한 줄이 실무에서 수없이 쓰입니다. 숫자가 아닌 문자열을 넣으면 NumberFormatException이 터진다는 것도 기억해 두세요. 1편에서 배운 try-catch가 필요한 대표 상황입니다.
단골 함정 — ==는 128부터 거짓말을 합니다
여기가 오늘의 함정입니다. 래퍼끼리 ==로 비교하면 이상한 일이 벌어집니다.
Integer a = 127, b = 127;
System.out.println(a == b); // true
Integer c = 128, d = 128;
System.out.println(c == d); // false ?!
값은 분명 같은데 127까지는 true, 128부터는 false입니다. 이유는 ==가 값이 아니라 상자(객체)가 같은 상자인지를 비교하기 때문입니다. 자바는 자주 쓰는 -128~127 범위의 Integer를 미리 만들어 재사용해서, 이 범위에서는 우연히 같은 상자를 가리켜 true가 나옵니다. 128부터는 매번 새 상자를 만들어 false가 되는 것이죠.

그림 1. 래퍼끼리 비교할 때는 equals를 씁니다.
해결은 간단합니다. **래퍼의 값 비교는 equals**로 합니다.
System.out.println(c.equals(d)); // true — 값을 비교
작은 수로 테스트할 때는 멀쩡하다가 데이터가 커지면 갑자기 어긋나는, 찾기 고약한 버그의 단골 원인입니다. 기본형 int끼리의 ==는 값 비교라 안전하니, 헷갈리면 "포장지가 붙어 있으면 equals"로 기억하면 됩니다.
날짜 — LocalDate로 오늘을 잡습니다
이제 두 번째 주제입니다. 날짜와 시간은 java.time 패키지의 LocalDate(날짜)와 LocalDateTime(날짜+시각)으로 다룹니다.
import java.time.LocalDate;
LocalDate today = LocalDate.now(); // 오늘
LocalDate exam = LocalDate.of(2026, 11, 19); // 특정 날짜 만들기
System.out.println(today.getYear()); // 연도만
System.out.println(exam.getDayOfWeek()); // 요일 (THURSDAY)
날짜 계산이 진짜 편리한 부분입니다. 더하고 빼는 메서드가 다 준비되어 있습니다.
LocalDate nextWeek = today.plusDays(7); // 7일 뒤
LocalDate lastMonth = today.minusMonths(1); // 한 달 전
한 가지 성질만 주의하세요. plusDays는 원본을 바꾸지 않고 새 날짜를 돌려줍니다. today.plusDays(7);만 쓰고 결과를 안 받으면 아무 일도 일어나지 않습니다. 반드시 LocalDate d = today.plusDays(7);처럼 받아서 써야 합니다.
두 날짜 사이의 간격은 ChronoUnit으로 구합니다. 디데이 계산이 한 줄입니다.
import java.time.temporal.ChronoUnit;
long dday = ChronoUnit.DAYS.between(today, exam); // 오늘부터 시험까지 며칠
직접 실행해 보기
오늘 배운 것을 한 번에 돌려 봅니다. 문자열 점수를 숫자로 바꿔 합산하고, 128 함정을 실험하고, 시험 디데이를 계산합니다. exam 날짜를 여러분의 실제 일정으로 바꿔 보세요.
import java.util.ArrayList;
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
public class Main {
public static void main(String[] args) {
// 1. 문자열 점수 → 숫자로 바꿔 합산 (parseInt)
String[] inputs = {"85", "92", "78"};
ArrayList<Integer> scores = new ArrayList<>();
int total = 0;
for (String s : inputs) {
int n = Integer.parseInt(s); // 문자열 → 숫자
scores.add(n); // 오토박싱으로 컬렉션에
total += n;
}
System.out.println("합계: " + total);
// 2. == 함정 실험: 127과 128
Integer a = 127, b = 127, c = 128, d = 128;
System.out.println("127==127 → " + (a == b));
System.out.println("128==128 → " + (c == d));
System.out.println("equals → " + c.equals(d));
// 3. 디데이 계산
LocalDate today = LocalDate.now();
LocalDate exam = LocalDate.of(2026, 11, 19); // 수능일
long dday = ChronoUnit.DAYS.between(today, exam);
System.out.println("시험까지 " + dday + "일");
}
}📱 코드 실행(실습)은 태블릿·PC에서 이용할 수 있어요. 모바일에서는 위 코드를 눈으로 따라가며 확인하세요.
두 번째 실험의 출력이 그림 1에서 본 그대로 나오는지 확인해 보세요. 같은 128인데 ==는 false, equals는 true가 나옵니다.
한 장 정리
| 항목 | 내용 |
|---|---|
| 래퍼 클래스 | 기본형을 감싼 클래스 — Integer, Double, Character, Boolean |
| 왜 필요한가 | 컬렉션·제네릭에는 클래스 타입만 올 수 있어서 |
| 오토박싱 | 포장·풀기를 컴파일러가 자동 처리 |
| 문자열→숫자 | Integer.parseInt("85") — 실패 시 NumberFormatException |
| 비교 함정 | 래퍼끼리 ==는 상자 비교(128부터 false) → equals 사용 |
| 날짜 | LocalDate.now() · of() · plusDays() — 원본 불변, 결과를 받아서 사용 |
| 디데이 | ChronoUnit.DAYS.between(시작, 끝) |
이것만 기억하면 됩니다. 래퍼는 기본형이 클래스 자리에 들어가기 위한 포장지이고, 포장은 자동이지만 비교만은 equals로 해야 합니다. 날짜는 LocalDate가 표준이고, 계산 결과는 늘 새 객체로 돌려받는다는 것만 지키면 디데이 계산까지 한 줄로 끝납니다.
미니 퀴즈
개념을 제대로 잡았는지 직접 풀어 봅니다. 정답은 눌러야 나옵니다.
미니 퀴즈
포켓코딩(JAVA3) 5편 · 다음 편: 파일 입출력 — 파일을 읽고 쓰는 법.