3주차 자료의 모든 토픽을 "기초 → 응용 → 실전" 순서로 재배열한 학습 경로.
1·2주차에서 컬렉션·I/O를 개론·내부 구조 수준으로 봤다면,
3주차는 전체 분류 지도 + 새 도구(제네릭/와일드카드/람다/스트림) 까지 확장한다.
[Phase 1] Pass by Value 진짜 이해 (C 포인터부터)
↓
[Phase 2] 컬렉션 프레임워크 전체 지도
↓ ↓
[Phase 3] 해시 원리 [Phase 4] 추상화 도구 (추상클래스 vs 인터페이스)
↓ ↓
[Phase 5] 제네릭과 와일드카드
↓
[Phase 6] 객체 비교 — Comparable & Comparator
↓
[Phase 7] I/O 시스템 큰 그림 (IO vs NIO)
↓
[Phase 8] Stream 실전 (InputStream/OutputStream/Reader/Writer)
↓
[Phase 9] I/O 강화 (Buffered/Data/Serialization)
↓
[Phase 10] 함수형 프로그래밍 — 람다와 스트림
총 10 Phase × 43 Unit — 분량이 많아 평일 1시간 × 2주 또는 압축 7일 일정 가능.
| 영역 | 1주차 (개론) | 2주차 (내부) | 3주차 (전체 지도 + 신규) |
|---|---|---|---|
| Pass by value | 자바는 항상 값 복사 | (없음) | C 포인터로 비교 + 객체 매개변수 2가지 패턴 |
| 컬렉션 | ArrayList vs LinkedList | 배열 확장 정책 | List/Set/Queue/Map 5계층 모두 + Vector, HashTable, ConcurrentHashMap |
| 해시 | LoadFactor 0.75 | (없음) | 해시 함수·충돌·체이닝·오픈 어드레싱 직접 구현 |
| I/O | 개념 + try-with-resources | (없음) | Stream vs Channel, Blocking vs Non-blocking, BufferedStream, DataStream, ObjectStream |
| 신규 | — | Reflection, Iterator | 제네릭/와일드카드, Comparable/Comparator, 람다, 스트림 |
| Day | Phase | 학습 목표 |
|---|---|---|
| 1일차 | Phase 1 + 2 | Pass by value 마무리 + 컬렉션 전체 지도 |
| 2일차 | Phase 3 + 4 | 해시 원리 + 추상클래스/인터페이스 |
| 3일차 | Phase 5 + 6 | 제네릭/와일드카드/PECS + 객체 비교 |
| 4일차 | Phase 7 | I/O 시스템 큰 그림 |
| 5일차 | Phase 8 | Stream 실전 코딩 |
| 6일차 | Phase 9 | Buffered/Data/Serialization |
| 7일차 | Phase 10 | 람다와 스트림 함수형 |
여유 일정 (14일): 각 Phase를 1~2일씩 배정하면 코드 실습까지 충분히 가능.
목표: 1주차에서 본 "Java는 항상 값 복사"의 의미를 C 포인터와 비교 해서 메모리 레벨까지 내려가 이해한다.
선수 지식: 1주차 Phase 4 (JVM 메모리)
핵심 개념
* 가 붙은 변수 = 메모리 주소를 저장하는 포인터 변수int *p = &a; (정수 변수 a의 주소를 p에 저장)*p (역참조 — p가 가리키는 주소의 실제 값)자기 점검
&a 와 *p 의 의미 차이는?선수 지식: Unit 1.1
핵심 개념
자기 점검
선수 지식: Unit 1.2
핵심 개념
자바 매개변수는 항상 값 복사:
핵심 패턴 2가지:
static void modify(MyObject obj) {
obj.value = 20; // ✅ 같은 객체의 필드 변경 → 호출자에 반영
obj = new MyObject(30); // ❌ 지역변수 obj가 새 객체 가리킴 → 호출자 무관
}
자기 점검
arg2 = arg1 (run 메서드 안에서) 이 호출자에 영향을 못 주는 이유는?원본 자료: 3주차, Java Call by Value 예제
목표: 1·2주차에서 부분적으로 본 컬렉션을 List/Set/Queue/Map 4대 계열의 전체 지도 로 확장한다.
선수 지식: 1주차 Phase 6
핵심 개념
Collection 인터페이스: List, Set, Queue가 상속Map은 별도 (key-value 구조)자기 점검
선수 지식: Unit 2.1
핵심 개념
| 구현체 | 특징 | 내부 구조 |
|---|---|---|
| HashSet | 순서 보장 X, 중복 X | HashMap 기반 |
| TreeSet | 자동 정렬(0-9, A-Z, a-z), 중복 X | Red-Black Tree |
| LinkedHashSet | 삽입 순서 유지, 중복 X | HashMap + LinkedList |
자기 점검
선수 지식: 2주차 Phase 5
핵심 개념
| 구현체 | 내부 | Thread Safe | 비고 |
|---|---|---|---|
| ArrayList | 배열 (1.5배 확장) | ❌ | 일반적으로 가장 많이 씀 |
| LinkedList | 이중 연결 리스트 | ❌ | Queue 인터페이스도 구현 |
| Vector | 배열 (ArrayList와 동일) | ✅ (synchronized) | 동기화로 인해 느림, 거의 안 씀 |
자기 점검
선수 지식: Unit 2.3
핵심 개념
주요 메서드 페어:
| 동작 | 안전 (실패 시 false/null) | 강제 (실패 시 예외) |
|---|---|---|
| 삽입 | offer() | add() |
| 조회 | peek() | element() |
| 제거 | poll() | remove() |
예시
Queue<Integer> queue = new LinkedList<>();
queue.offer(1);
queue.offer(2);
queue.poll(); // 1 반환 후 제거
queue.peek(); // 2 (제거 안 함)
자기 점검
add() 와 offer() 의 차이는?선수 지식: Unit 2.4
핵심 개념
| 구현체 | 순서 | null 허용 | Thread Safe |
|---|---|---|---|
| HashMap | ❌ | 키 1개, 값 다수 | ❌ |
| LinkedHashMap | ✅ 삽입순 | HashMap과 동일 | ❌ |
| TreeMap | ✅ 키 정렬 | 값만 (키는 ❌) | ❌ |
| HashTable | ❌ | ❌ (키·값 모두) | ✅ (메서드 동기화) |
| ConcurrentHashMap | ❌ | ❌ | ✅ (동시성 최적화) |
자기 점검
선수 지식: Unit 2.1 ~ 2.5
전체 계층:
Collection ─┬─ List ──┬─ ArrayList
│ ├─ LinkedList
│ └─ Vector
├─ Set ──┬─ HashSet
│ ├─ TreeSet
│ └─ LinkedHashSet
└─ Queue ─┬─ LinkedList (List도 구현)
├─ ArrayDeque
└─ PriorityQueue
Map (Collection X) ─┬─ HashMap
├─ LinkedHashMap
├─ TreeMap
├─ HashTable
└─ ConcurrentHashMap
자기 점검
목표: HashMap·HashSet의 내부에서 일어나는 일을 직접 구현해보면서 이해한다.
선수 지식: Phase 2
핵심 개념
예시 해시 함수:
해시값 = 이름의 첫 글자 ASCII % 10
Alice → 65 % 10 = 5 (위치 5에 저장)
Bob → 66 % 10 = 6 (위치 6에 저장)
자기 점검
선수 지식: Unit 3.1
핵심 개념
자기 점검
선수 지식: Unit 3.2
핵심 개념
Index 5 → [("Alice", 1234), ("Alex", 5678)]핵심 동작 (직접 구현):
put: 해시값 계산 → 버킷 → 키 존재하면 갱신, 없으면 리스트에 추가get: 해시값 계산 → 버킷 탐색 → 키 일치하는 노드 반환자기 점검
선수 지식: Unit 3.3
핵심 개념
자기 점검
목표: 두 추상화 도구의 정확한 차이와 선택 기준을 잡는다.
선수 지식: 1주차 Phase 2 (상속)
핵심 개념
abstract 명시자기 점검
선수 지식: Unit 4.1
핵심 개념
public static final 상수interface Constants {
int MAX_VALUE = 100; // = public static final int MAX_VALUE = 100;
}자기 점검
public static final 인 이유는?선수 지식: Unit 4.2
핵심 개념
default 메서드: 인스턴스 메서드처럼 호출static 메서드: 인터페이스명으로 호출interface Vehicle {
void move();
default void start() { System.out.println("starting"); }
static void stop() { System.out.println("stopped"); }
}
자기 점검
선수 지식: Unit 4.1 ~ 4.3
핵심 비교표:
| 항목 | 추상클래스 | 인터페이스 |
|---|---|---|
| 다중 상속 | ❌ | ✅ |
| 구현 메서드 | ✅ | Java 8+ default만 |
| 필드 | 자유 | public static final 만 |
| 생성자 | ✅ | ❌ |
| 사용 의도 | "is-a" 강한 관계 | "can-do" 능력 |
선택 가이드:
자기 점검
List 인터페이스와 AbstractList 추상클래스가 둘 다 존재하는 이유는?목표: 제네릭의 불공변성을 이해하고 PECS 원칙으로 와일드카드를 자유자재로 쓴다.
선수 지식: Phase 4
핵심 개념
String은 Object의 자식 → String[]은 Object[]로 취급 가능 (배열은 공변)List<String>은 List<Object>로 취급 불가 (제네릭은 불공변)List<String> stringList = ...;
List<Object> objList = stringList; // 컴파일 에러자기 점검
선수 지식: Unit 5.1
핵심 개념
List<?> = "어떤 타입이든 OK"List<String>, List<Integer> 모두 List<?> 에 대입 가능Object로만 다룰 수 있음public static void processList(List<?> list) {
for (Object item : list) {
if (item instanceof String s) { ... }
}
}
자기 점검
List<?> 와 List<Object> 의 차이는?List<?> 에 어떤 원소도 add할 수 없는 이유는? (힌트: 타입 안전성)선수 지식: Unit 5.2
핵심 개념
Box<? extends Fruit>: T가 Fruit이거나 Fruit의 자식Box<? extends Fruit> box1 = new Box<Apple>(); // OK
Box<? extends Fruit> box2 = new Box<Banana>(); // OK
자기 점검
extends Fruit 인데 왜 add는 못 하는가?extends를 쓰는 게 안전한가?선수 지식: Unit 5.3
핵심 개념
Box<? super Fruit>: T가 Fruit이거나 Fruit의 부모Box<? super Fruit> box1 = new Box<Fruit>();
Box<? super Fruit> box2 = new Box<Object>();
자기 점검
super Fruit 인데 왜 꺼낼 때는 Object로만 받는가?선수 지식: Unit 5.4
핵심 개념
// Producer: 컬렉션에서 꺼내기 (extends)
void printAll(Collection<? extends B> c) {
for (B e : c) System.out.println(e);
}
// Consumer: 컬렉션에 넣기 (super)
void addElement(Collection<? super B> c) {
c.add(new B());
}
규칙 요약:
extendssuper자기 점검
Collections.copy(dest, src) 의 시그니처에서 src와 dest는 각각 어느 쪽인가?목표: 자바에서 객체의 정렬 기준을 어떻게 정의하는지 두 가지 방식으로 익힌다.
선수 지식: Phase 5
핵심 개념
<, > 로 비교 가능자기 점검
obj1 > obj2 가 자바에서 컴파일 에러인 이유는?선수 지식: Unit 6.1
핵심 개념
Comparable<T> 인터페이스 구현compareTo(T o) 한 메서드만 재정의Collections.sort() 가 자동 사용class Member implements Comparable<Member> {
int age;
public int compareTo(Member o) {
return this.age - o.age; // 나이 오름차순
}
}
자기 점검
Integer, String, LocalDate 가 모두 Comparable인 이유는?equals() 와 compareTo() 의 일관성을 깨면 어떤 문제가 생기는가?선수 지식: Unit 6.2
핵심 개념
Comparator<T> 인터페이스compare(T o1, T o2) 한 메서드만 재정의Collections.sort(list, comparator), list.sort(comparator) 에 전달Comparator<Member> byAge = (a, b) -> a.age - b.age;
Comparator<Member> byName = Comparator.comparing(m -> m.name);
list.sort(byAge);
list.sort(byName);
자기 점검
Comparator.reversed() 는 무엇을 하는가?선수 지식: Unit 6.3
핵심 비교:
| 항목 | Comparable | Comparator |
|---|---|---|
| 정의 위치 | 클래스 내부 | 클래스 외부 |
| 메서드 | compareTo(T o) | compare(T o1, T o2) |
| 비교 대상 | this vs 매개변수 | 매개변수 1 vs 매개변수 2 |
| 정렬 기준 수 | 1개 (기본) | 무제한 |
선택 가이드:
목표: I/O의 전체 풍경(IO vs NIO, Stream vs Channel, Blocking vs Non-blocking)을 잡는다.
선수 지식: 1주차 Phase 7
핵심 개념
자기 점검
선수 지식: Unit 7.1
핵심 개념
| 구분 | IO (Java 1.0~) | NIO (Java 1.4+) | NIO.2 (Java 7+) |
|---|---|---|---|
| 단위 | 스트림 (1바이트씩) | 채널 + 버퍼 (블록) | 채널 + 버퍼 |
| 방향 | 단방향 (In/Out 분리) | 양방향 | 양방향 |
| Blocking | 항상 Blocking | Non-blocking 가능 | Non-blocking 가능 |
| 파일 API | File 클래스 | FileChannel | Files (static), Path |
File vs Files 핵심 차이:
File: 객체 생성 필요, 심볼릭 링크 등 일부 기능 부재Files: 모든 메서드 static, 풍부한 API자기 점검
File 이 "객체 생성 후 사용"인 데 비해 Files 가 "static"인 게 어떤 장점을 주는가?선수 지식: Unit 7.2
핵심 개념
| 항목 | Stream (IO) | Channel (NIO) |
|---|---|---|
| 데이터 단위 | byte / byte[] | Buffer (블록) |
| 방향 | 단방향 (In 또는 Out) | 양방향 |
| 버퍼 | 기본 non-buffer (보조 스트림으로 추가) | 항상 Buffer 통해서 |
| 동작 | Blocking | Blocking 또는 Non-blocking |
핵심 포인트
자기 점검
선수 지식: Unit 7.3
핵심 개념
Blocking IO:
read() 호출 → 데이터가 올 때까지 스레드 정지Non-blocking NIO:
자기 점검
선수 지식: Unit 7.4
핵심 개념
오버헤드:
File 객체 (전통 IO):
File file = new File(path, fileName);
file.createNewFile(); // 파일 생성
file.getAbsolutePath(); // 절대경로
file.getCanonicalPath(); // 정규화된 경로
file.getName(); // 파일명
file.getParent(); // 부모 디렉터리
자기 점검
목표: 바이트 스트림과 문자 스트림의 차이를 손에 익히고 한글 처리까지 자유롭게 한다.
선수 지식: Phase 7
핵심 개념
System.in = 표준 입력 스트림 (보통 키보드)InputStream 타입read() 는 1바이트씩 읽음한글이 안 되는 이유:
read() 한 번으로는 깨짐자기 점검
선수 지식: Unit 8.1
핵심 개념
InputStream을 상속한 파일 전용 바이트 스트림read(): 1바이트 읽기 (반환은 int)-1 반환try (FileInputStream fis = new FileInputStream("input.txt")) {
int b;
while ((b = fis.read()) != -1) {
System.out.print((char) b);
}
}
자기 점검
read() 의 반환 타입이 byte가 아니라 int인 이유는? (힌트: -1을 어떻게 표현?)선수 지식: Unit 8.2
핵심 개념
byte[] 버퍼로 한 번에 여러 바이트read(byte[] buf) 는 읽은 바이트 수 반환byte[] buf = new byte[10];
int n;
while ((n = fis.read(buf)) != -1) {
for (int k = 0; k < n; k++) { // ← n까지만! buf.length가 아님
System.out.print((char) buf[k]);
}
}
자기 점검
for (byte b : buf) 로 순회하면 마지막에 어떤 문제가 생기는가?선수 지식: Unit 8.3
핵심 개념
OutputStream을 상속write(int b): 1바이트 쓰기new FileOutputStream(path, true) → 이어쓰기 모드try (FileOutputStream fos = new FileOutputStream("output.txt", true)) {
fos.write(65); // 'A'
fos.write(66); // 'B'
}
바이트가 영어로 보이는 이유:
자기 점검
write(byte) 로 1바이트씩 쓰면 왜 깨지는가?선수 지식: Unit 8.4
핵심 개념
두 가지 방법:
// 방법 1: FileReader (직접)
try (FileReader fr = new FileReader("reader.txt")) {
int c;
while ((c = fr.read()) != -1) System.out.print((char) c);
}
// 방법 2: InputStreamReader (보조 스트림)
try (FileInputStream fis = new FileInputStream("reader.txt");
InputStreamReader isr = new InputStreamReader(fis)) {
int c;
while ((c = isr.read()) != -1) System.out.print((char) c);
}
자기 점검
FileReader 와 InputStreamReader 의 차이는?선수 지식: Unit 8.5
핵심 개념
Writer 의 파일용 구현체write(int), write(char[]), write(String), write(char[], offset, length) 다양try (FileWriter fw = new FileWriter("writer.txt")) {
fw.write('A');
fw.write(new char[]{'B', 'C', 'D'});
fw.write("안녕하세요");
}
자기 점검
FileWriter 와 FileOutputStream 을 한글 데이터에 각각 쓰면 결과는?목표: 기본 스트림에 보조 스트림을 입혀 성능·편의성·구조화를 모두 잡는다.
선수 지식: 1주차 Phase 7
핵심 개념
try (자원선언) 구문close() 호출AutoCloseable 인터페이스 구현try (FileInputStream fis = new FileInputStream("a.txt");
FileOutputStream fos = new FileOutputStream("b.txt")) {
// ...
} // fos.close() → fis.close() 순으로 호출
자기 점검
finally 블록에서 close 하는 것보다 try-with-resources가 더 안전한 이유는?선수 지식: Phase 8
핵심 개념
try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(src));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(dst))) {
byte[] buf = new byte[1024];
int n;
while ((n = bis.read(buf)) != -1) bos.write(buf, 0, n);
}
자기 점검
선수 지식: Unit 9.2
핵심 개념
int, double, boolean, String)을 타입별로 저장/읽기DataStream 없이 (CSV 방식):
writer.write(name + "," + age + "," + score); // 매번 파싱 필요
DataStream 사용:
dos.writeUTF(name);
dos.writeInt(age);
dos.writeDouble(score);
// 읽을 때
String name = dis.readUTF();
int age = dis.readInt();
double score = dis.readDouble();
자기 점검
선수 지식: 1주차 Phase 7 + Unit 9.3
핵심 개념
Serializable (마커 인터페이스) 구현ObjectOutputStream.writeObject(obj) / ObjectInputStream.readObject()transient 필드는 직렬화 제외 (1주차에 학습)예시:
class Person implements Serializable {
String name;
transient String job; // 제외됨
}
oos.writeObject(personLee); // 저장
Person p = (Person) ois.readObject(); // 복원
자기 점검
transient 가 적용된 필드는 역직렬화 후 어떤 값이 되는가?선수 지식: Unit 9.4
핵심 개념
public class MyClass implements Serializable {
private static final long serialVersionUID = 1L;
// ...
}
자기 점검
serialVersionUID 명시 누락이 어떤 사고로 이어지는가?serialVersionUID 누락에 경고를 띄우는 이유는?목표: 함수를 값으로 다루는 새 패러다임으로 컬렉션 처리를 선언적으로 한다.
선수 지식: Phase 4
핵심 개념
@FunctionalInterface 어노테이션 (강제 검증)자바 표준 함수형 인터페이스:
| 인터페이스 | 시그니처 | 용도 |
|---|---|---|
Function<T, R> | R apply(T) | 변환 |
Predicate<T> | boolean test(T) | 조건 |
Consumer<T> | void accept(T) | 소비 |
Supplier<T> | T get() | 공급 |
자기 점검
Runnable, Comparator, Comparable 도 함수형 인터페이스인가?선수 지식: Unit 10.1
핵심 개념
// 전통: 익명 클래스
Comparator<Integer> c1 = new Comparator<Integer>() {
public int compare(Integer a, Integer b) { return a - b; }
};
// 람다
Comparator<Integer> c2 = (a, b) -> a - b;
자기 점검
선수 지식: Unit 10.2, Phase 2
핵심 개념
중간 연산 (Stream 반환):
filter, map, sorted, distinct, limit, skip최종 연산 (값 또는 void 반환):
forEach, collect, count, reduce, anyMatchsList.stream()
.filter(s -> s.length() > 3)
.map(String::toUpperCase)
.sorted()
.forEach(System.out::println);
자기 점검
선수 지식: Unit 10.3
핵심 패턴:
// 1. 필터링 + 변환 + 수집
List<Integer> lengths = sList.stream()
.filter(s -> !s.isEmpty())
.map(String::length)
.collect(Collectors.toList());
// 2. 통계
int total = nums.stream().mapToInt(Integer::intValue).sum();
double avg = nums.stream().mapToInt(Integer::intValue).average().orElse(0);
// 3. 그룹핑
Map<Integer, List<String>> byLength = sList.stream()
.collect(Collectors.groupingBy(String::length));
자기 점검
stream() 과 parallelStream() 의 차이와 주의점은?Collectors.toList() 와 .toList() (Java 16+)의 차이는?*p와 &a 의 의미를 설명하라arg2 = arg1 가 호출자에 영향을 안 주는 이유를 스택 프레임으로 설명하라public static final 인 이유는?List<String> 이 List<Object> 가 아닌 이유는?Box<? extends Fruit> 에 add를 못 하는 이유는?compareTo 의 반환값 규칙은?read() 1바이트로는 안 읽히는 이유는?serialVersionUID 를 명시해야 하는 이유는?반드시 깊이 파기:
[ ] Phase 1 — Pass by Value 정복 (Unit 1.1~1.3)
[ ] Phase 2 — 컬렉션 전체 지도 (Unit 2.1~2.6)
[ ] Phase 3 — 해시의 원리 (Unit 3.1~3.4)
[ ] Phase 4 — 추상클래스 vs 인터페이스 (Unit 4.1~4.4)
[ ] Phase 5 — 제네릭과 와일드카드 (Unit 5.1~5.5)
[ ] Phase 6 — 객체 비교 (Unit 6.1~6.4)
[ ] Phase 7 — I/O 시스템 큰 그림 (Unit 7.1~7.5)
[ ] Phase 8 — Stream 실전 (Unit 8.1~8.6)
[ ] Phase 9 — I/O 강화 (Unit 9.1~9.5)
[ ] Phase 10 — 함수형 프로그래밍 (Unit 10.1~10.4)
[ ] 종합 자기 점검 31문항 통과