🎯 F-lab Java 3주차 학습 커리큘럼

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주차와의 관계

영역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, 람다, 스트림

🗓️ 권장 학습 일정 (압축 7일)

DayPhase학습 목표
1일차Phase 1 + 2Pass by value 마무리 + 컬렉션 전체 지도
2일차Phase 3 + 4해시 원리 + 추상클래스/인터페이스
3일차Phase 5 + 6제네릭/와일드카드/PECS + 객체 비교
4일차Phase 7I/O 시스템 큰 그림
5일차Phase 8Stream 실전 코딩
6일차Phase 9Buffered/Data/Serialization
7일차Phase 10람다와 스트림 함수형

여유 일정 (14일): 각 Phase를 1~2일씩 배정하면 코드 실습까지 충분히 가능.


📚 Phase 1 — Pass by Value의 진짜 이해

목표: 1주차에서 본 "Java는 항상 값 복사"의 의미를 C 포인터와 비교 해서 메모리 레벨까지 내려가 이해한다.

Unit 1.1 — C 포인터 기초

선수 지식: 1주차 Phase 4 (JVM 메모리)

핵심 개념

  • * 가 붙은 변수 = 메모리 주소를 저장하는 포인터 변수
  • 선언과 초기화: int *p = &a; (정수 변수 a의 주소를 p에 저장)
  • 값 꺼내기: *p (역참조 — p가 가리키는 주소의 실제 값)
  • C는 주소를 직접 다룰 수 있다

자기 점검

  • &a*p 의 의미 차이는?
  • C에서 포인터를 잘못 다루면 어떤 사고가 발생할 수 있는가?

Unit 1.2 — Pass by value vs Pass by reference

선수 지식: Unit 1.1

핵심 개념

  • Pass by value: 값을 복사해서 전달 → 호출된 메서드 내 변경이 원본에 영향 없음
  • Pass by reference (C++ 등): 변수 자체의 별칭(주소)을 전달 → 변경이 원본에 반영
  • 자바는 Pass by value만 존재

자기 점검

  • C++의 reference와 C의 포인터는 어떤 차이가 있는가?
  • 진정한 pass by reference 언어에서 변수 자체가 새 객체로 재할당되면 호출자에게 어떻게 보이는가?

Unit 1.3 — 자바의 Call by Value 한 가지

선수 지식: Unit 1.2

핵심 개념

자바 매개변수는 항상 값 복사:

  • 원시 타입: 값 자체 복사
  • 객체 타입: Heap의 주소값이 복사 (포인터 비슷하지만 직접 조작 불가)

핵심 패턴 2가지:

static void modify(MyObject obj) {
    obj.value = 20;        // ✅ 같은 객체의 필드 변경 → 호출자에 반영
    obj = new MyObject(30); // ❌ 지역변수 obj가 새 객체 가리킴 → 호출자 무관
}

자기 점검

  • "주소값이 복사된다"와 "Pass by reference"의 차이는?
  • arg2 = arg1 (run 메서드 안에서) 이 호출자에 영향을 못 주는 이유는?

원본 자료: 3주차, Java Call by Value 예제


📚 Phase 2 — 컬렉션 프레임워크 전체 지도

목표: 1·2주차에서 부분적으로 본 컬렉션을 List/Set/Queue/Map 4대 계열의 전체 지도 로 확장한다.

Unit 2.1 — 배열의 한계와 컬렉션의 등장

선수 지식: 1주차 Phase 6

핵심 개념

  • 배열의 불편함:
    • 크기 지정 후 변경 불가
    • 중간 삽입/삭제 메서드 없음
  • Collection = 자료구조 + 알고리즘을 클래스로 구조화
  • Collection 인터페이스: List, Set, Queue가 상속
  • Map은 별도 (key-value 구조)

자기 점검

  • 배열도 가능한 작업을 굳이 컬렉션으로 하는 이유는?
  • Map이 Collection을 상속하지 않는 이유는?

Unit 2.2 — Set 3형제 (HashSet / TreeSet / LinkedHashSet)

선수 지식: Unit 2.1

핵심 개념

구현체특징내부 구조
HashSet순서 보장 X, 중복 XHashMap 기반
TreeSet자동 정렬(0-9, A-Z, a-z), 중복 XRed-Black Tree
LinkedHashSet삽입 순서 유지, 중복 XHashMap + LinkedList

자기 점검

  • HashSet은 어떻게 중복을 검사하는가? (힌트: hashCode + equals)
  • TreeSet의 삽입 비용은? (힌트: O(log n))

Unit 2.3 — List 3형제 (ArrayList / LinkedList / Vector)

선수 지식: 2주차 Phase 5

핵심 개념

구현체내부Thread Safe비고
ArrayList배열 (1.5배 확장)일반적으로 가장 많이 씀
LinkedList이중 연결 리스트Queue 인터페이스도 구현
Vector배열 (ArrayList와 동일)✅ (synchronized)동기화로 인해 느림, 거의 안 씀
  • LinkedList는 이중 연결 리스트 → 양방향 탐색 가능
  • LinkedList가 List + Queue 둘 다 구현 → Queue 용도로 자주 쓰임

자기 점검

  • Vector가 거의 사용되지 않는 이유는? (힌트: 더 나은 대안)
  • LinkedList를 Queue로 쓸 때의 장점은?

Unit 2.4 — Queue (FIFO 자료구조)

선수 지식: Unit 2.3

핵심 개념

  • FIFO (First In First Out) 구조
  • null 삽입 불가
  • 사용처: BFS 그래프 탐색, 컴퓨터 버퍼, 메시지 큐(MQ)

주요 메서드 페어:

동작안전 (실패 시 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() 의 차이는?
  • LinkedList로 Queue를 만드는데 ArrayDeque를 권장하는 이유는?

Unit 2.5 — Map 5형제

선수 지식: Unit 2.4

핵심 개념

구현체순서null 허용Thread Safe
HashMap키 1개, 값 다수
LinkedHashMap✅ 삽입순HashMap과 동일
TreeMap✅ 키 정렬값만 (키는 ❌)
HashTable❌ (키·값 모두)✅ (메서드 동기화)
ConcurrentHashMap✅ (동시성 최적화)
  • TreeMap 정렬 순서: 숫자 → 대문자 → 소문자 → 한글
  • HashTable은 거의 안 쓰고 ConcurrentHashMap 권장

자기 점검

  • HashMap이 멀티스레드에 안전하지 않은 이유는?
  • ConcurrentHashMap이 HashTable보다 빠른 이유는? (힌트: 락 단위)

Unit 2.6 — 컬렉션 전체 지도 정리

선수 지식: 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

자기 점검

  • "정렬된 키"가 필요한 시나리오는? (TreeMap 후보)
  • "삽입 순서 유지 + 중복 제거"가 필요한 시나리오는? (LinkedHashSet 후보)

📚 Phase 3 — 해시(Hash)의 원리

목표: HashMap·HashSet의 내부에서 일어나는 일을 직접 구현해보면서 이해한다.

Unit 3.1 — 해시의 탄생 배경

선수 지식: Phase 2

핵심 개념

  • 데이터가 많으면 순차 검색은 O(n) → 너무 느림
  • 질문: "수학적으로 처리해서 위치를 바로 알 수 없을까?"
  • 답: 해시 함수 (key → 정수 인덱스)

예시 해시 함수:

해시값 = 이름의 첫 글자 ASCII % 10
Alice → 65 % 10 = 5  (위치 5에 저장)
Bob   → 66 % 10 = 6  (위치 6에 저장)

자기 점검

  • 해시 검색의 시간 복잡도는? (이상적 케이스)
  • 해시 함수의 좋은 조건 3가지는? (힌트: 빠름, 균등 분포, 결정적)

Unit 3.2 — 해시 충돌(Hash Collision)

선수 지식: Unit 3.1

핵심 개념

  • 서로 다른 입력이 같은 해시값 을 가지는 상황
  • 유한한 출력 크기 → 입력이 많으면 충돌은 불가피
  • 충돌이 잦으면 해시의 장점(O(1)) 무너짐 → 최악 O(n)

자기 점검

  • 비둘기집 원리(Pigeonhole Principle)와 해시 충돌의 관계는?
  • 충돌이 절대 안 나는 해시 함수가 가능한가? (힌트: Perfect Hashing)

Unit 3.3 — 충돌 해결법 1: 체이닝(Chaining)

선수 지식: Unit 3.2

핵심 개념

  • 같은 인덱스에 여러 데이터가 들어오면 연결 리스트로 연결
  • Index 5 → [("Alice", 1234), ("Alex", 5678)]
  • Java 8부터 한 버킷의 항목이 8개 넘으면 트리(Red-Black)로 변환

핵심 동작 (직접 구현):

  • put: 해시값 계산 → 버킷 → 키 존재하면 갱신, 없으면 리스트에 추가
  • get: 해시값 계산 → 버킷 탐색 → 키 일치하는 노드 반환

자기 점검

  • 체이닝의 시간 복잡도가 최악일 때 O(n)인 이유는?
  • Java 8의 트리 변환은 어떤 문제를 해결하는가?

Unit 3.4 — 충돌 해결법 2: 오픈 어드레싱(Open Addressing)

선수 지식: Unit 3.3

핵심 개념

  • 충돌 시 빈 버킷을 찾아 저장
  • 탐사 방식: 선형 탐사, 이차 탐사, 이중 해싱
  • 별도 자료구조 불필요 → 메모리 효율 ↑
  • 단점: 클러스터링 발생 가능

자기 점검

  • 체이닝과 오픈 어드레싱 중 자바 HashMap이 채택한 방식은?
  • 삭제가 오픈 어드레싱에서 까다로운 이유는? (힌트: 탐사 체인)

📚 Phase 4 — 추상화의 두 도구 (추상클래스 vs 인터페이스)

목표: 두 추상화 도구의 정확한 차이와 선택 기준을 잡는다.

Unit 4.1 — 추상클래스의 특징

선수 지식: 1주차 Phase 2 (상속)

핵심 개념

  • 추상 메서드(구현 없는 메서드)가 1개라도 있으면 abstract 명시
  • 구현된 메서드도 포함 가능
  • 단일 상속만
  • 생성자 가질 수 있음 (단, 직접 인스턴스화는 불가)

자기 점검

  • 추상클래스의 생성자는 언제 호출되는가?
  • "구현된 메서드도 가질 수 있다"가 인터페이스 대비 어떤 이점을 주는가?

Unit 4.2 — 인터페이스의 특징

선수 지식: Unit 4.1

핵심 개념

  • 일반적으로 구현된 메서드 없음 (Java 8 전)
  • 다중 상속(구현) 가능
  • 생성자 가질 수 없음
  • 필드는 자동으로 public static final 상수
    interface Constants {
        int MAX_VALUE = 100;  // = public static final int MAX_VALUE = 100;
    }

자기 점검

  • 인터페이스 필드가 public static final 인 이유는?
  • 인터페이스만 구현한 클래스를 인스턴스화하면 무엇이 호출되는가?

Unit 4.3 — Java 8 default & static 메서드

선수 지식: Unit 4.2

핵심 개념

  • Java 8부터 인터페이스에 구현 포함 메서드 가능
    • default 메서드: 인스턴스 메서드처럼 호출
    • static 메서드: 인터페이스명으로 호출
  • 등장 이유: 기존 인터페이스에 새 메서드 추가 시 모든 구현체가 깨지는 문제 해결
interface Vehicle {
    void move();
    default void start() { System.out.println("starting"); }
    static void stop() { System.out.println("stopped"); }
}

자기 점검

  • default 메서드 도입 이전엔 인터페이스 진화가 왜 어려웠는가?
  • 추상클래스 vs 인터페이스의 경계가 모호해진 이유는?

Unit 4.4 — 추상클래스 vs 인터페이스 선택 기준

선수 지식: Unit 4.1 ~ 4.3

핵심 비교표:

항목추상클래스인터페이스
다중 상속
구현 메서드Java 8+ default만
필드자유public static final 만
생성자
사용 의도"is-a" 강한 관계"can-do" 능력

선택 가이드:

  • 공통 상태 + 공통 구현이 있는 계층: 추상클래스
  • 여러 능력의 조합이 필요: 인터페이스
  • 둘 다 가능하면 인터페이스 우선 (Spring, JPA 모두 인터페이스 우선)

자기 점검

  • List 인터페이스와 AbstractList 추상클래스가 둘 다 존재하는 이유는?

📚 Phase 5 — 제네릭과 와일드카드

목표: 제네릭의 불공변성을 이해하고 PECS 원칙으로 와일드카드를 자유자재로 쓴다.

Unit 5.1 — 제네릭의 불공변성(Invariance)

선수 지식: Phase 4

핵심 개념

  • StringObject의 자식 → String[]Object[]로 취급 가능 (배열은 공변)
  • 하지만 List<String>List<Object>로 취급 불가 (제네릭은 불공변)
    List<String> stringList = ...;
    List<Object> objList = stringList;  // 컴파일 에러
  • 이유: 타입 안전성 (Object 타입으로 받으면 Integer를 넣을 수 있어버림)

자기 점검

  • 제네릭이 공변이었다면 어떤 사고가 가능했을까?
  • 배열은 왜 공변으로 설계됐는가? (역사적 배경)

Unit 5.2 — 무제한 와일드카드 (?)

선수 지식: 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.3 — 상한 경계 와일드카드 (? extends T)

선수 지식: Unit 5.2

핵심 개념

  • Box<? extends Fruit>: T가 Fruit이거나 Fruit의 자식
  • 읽기 전용 (Producer) 역할에 사용
  • 안에서 꺼내는 건 안전 (모두 Fruit으로 다룰 수 있음)
  • 안에 넣는 건 불가능 (정확한 타입을 모르므로)
Box<? extends Fruit> box1 = new Box<Apple>();   // OK
Box<? extends Fruit> box2 = new Box<Banana>();  // OK

자기 점검

  • extends Fruit 인데 왜 add는 못 하는가?
  • 데이터를 "꺼내기만" 한다면 항상 extends를 쓰는 게 안전한가?

Unit 5.4 — 하한 경계 와일드카드 (? super T)

선수 지식: Unit 5.3

핵심 개념

  • Box<? super Fruit>: T가 Fruit이거나 Fruit의 부모
  • 쓰기 전용 (Consumer) 역할에 사용
  • 안에 Fruit (또는 자식)을 넣는 건 안전
  • 꺼낼 때는 Object로만
Box<? super Fruit> box1 = new Box<Fruit>();
Box<? super Fruit> box2 = new Box<Object>();

자기 점검

  • super Fruit 인데 왜 꺼낼 때는 Object로만 받는가?

Unit 5.5 — PECS 원칙 (Producer-Extends, Consumer-Super)

선수 지식: 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());
}

규칙 요약:

  • 데이터를 꺼낸다 (Produce) → extends
  • 데이터를 넣는다 (Consume) → super

자기 점검

  • Collections.copy(dest, src) 의 시그니처에서 src와 dest는 각각 어느 쪽인가?
  • 양쪽 다 필요하면 어떻게 해야 하는가? (힌트: T 자체)

📚 Phase 6 — 객체 비교 (Comparable & Comparator)

목표: 자바에서 객체의 정렬 기준을 어떻게 정의하는지 두 가지 방식으로 익힌다.

Unit 6.1 — 왜 별도의 비교 인터페이스가 필요한가

선수 지식: Phase 5

핵심 개념

  • 원시 타입: <, > 로 비교 가능
  • 객체: 어떤 필드를 기준으로 비교할지 모호 → 자체적으로 정의 필요
  • 두 가지 방법: Comparable (자기 자신) vs Comparator (외부에서)

자기 점검

  • obj1 > obj2 가 자바에서 컴파일 에러인 이유는?

Unit 6.2 — Comparable: 자기 자신과 비교

선수 지식: Unit 6.1

핵심 개념

  • Comparable<T> 인터페이스 구현
  • compareTo(T o) 한 메서드만 재정의
  • 반환값:
    • 음수: this < o
    • 0: this == o (정렬상)
    • 양수: this > o
  • 클래스의 기본 정렬 순서(natural ordering) 정의
  • TreeSet, TreeMap 키, 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.3 — Comparator: 외부에서 비교 기준 주입

선수 지식: 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);

자기 점검

  • 같은 데이터를 여러 기준으로 정렬해야 한다면 Comparable과 Comparator 중 무엇이 적합한가?
  • Comparator.reversed() 는 무엇을 하는가?

Unit 6.4 — Comparable vs Comparator 선택

선수 지식: Unit 6.3

핵심 비교:

항목ComparableComparator
정의 위치클래스 내부클래스 외부
메서드compareTo(T o)compare(T o1, T o2)
비교 대상this vs 매개변수매개변수 1 vs 매개변수 2
정렬 기준 수1개 (기본)무제한

선택 가이드:

  • 클래스의 자연스러운 순서가 있다: Comparable
  • 상황별로 다른 정렬이 필요하다: Comparator
  • 클래스 수정 권한이 없다: Comparator만 가능

📚 Phase 7 — I/O 시스템의 큰 그림

목표: I/O의 전체 풍경(IO vs NIO, Stream vs Channel, Blocking vs Non-blocking)을 잡는다.

Unit 7.1 — I/O란 무엇인가

선수 지식: 1주차 Phase 7

핵심 개념

  • I = Input, O = Output
  • JVM 기준:
    • 외부 데이터를 읽으면 → Input
    • 외부로 보내면 → Output
  • 이 "기준점이 JVM"이라는 것이 매우 중요 (헷갈리는 포인트)

자기 점검

  • 콘솔에 출력한다는 건 Input인가 Output인가?
  • 데이터베이스에서 읽는 건 Input인가 Output인가?

Unit 7.2 — IO vs NIO (역사적 진화)

선수 지식: Unit 7.1

핵심 개념

구분IO (Java 1.0~)NIO (Java 1.4+)NIO.2 (Java 7+)
단위스트림 (1바이트씩)채널 + 버퍼 (블록)채널 + 버퍼
방향단방향 (In/Out 분리)양방향양방향
Blocking항상 BlockingNon-blocking 가능Non-blocking 가능
파일 APIFile 클래스FileChannelFiles (static), Path

File vs Files 핵심 차이:

  • File: 객체 생성 필요, 심볼릭 링크 등 일부 기능 부재
  • Files: 모든 메서드 static, 풍부한 API

자기 점검

  • 어떤 상황에 IO를, 어떤 상황에 NIO를 써야 하는가?
  • File 이 "객체 생성 후 사용"인 데 비해 Files 가 "static"인 게 어떤 장점을 주는가?

Unit 7.3 — Stream vs Channel

선수 지식: Unit 7.2

핵심 개념

항목Stream (IO)Channel (NIO)
데이터 단위byte / byte[]Buffer (블록)
방향단방향 (In 또는 Out)양방향
버퍼기본 non-buffer (보조 스트림으로 추가)항상 Buffer 통해서
동작BlockingBlocking 또는 Non-blocking

핵심 포인트

  • Channel은 항상 Buffer 통해서만 데이터 입출력
  • Channel에서 읽기 → Buffer에 담음 → 처리
  • Channel에 쓰기 → Buffer에 담음 → Channel에 flush

자기 점검

  • "양방향"이 운영상 어떤 이점을 주는가?
  • Buffer가 NIO의 필수 요소인 이유는?

Unit 7.4 — Blocking vs Non-blocking

선수 지식: Unit 7.3

핵심 개념

Blocking IO:

  • read() 호출 → 데이터가 올 때까지 스레드 정지
  • 빠져나오는 유일한 방법: 스트림 close()
  • 인터럽트도 안 통함

Non-blocking NIO:

  • 데이터가 없으면 즉시 0 반환 (스레드 정지 X)
  • 핵심 객체: Selector (멀티플렉서) — 여러 채널 중 준비된 것만 골라 처리
  • 한 스레드가 수많은 연결 처리 가능

자기 점검

  • 동시 접속 1만 명을 Blocking IO로 처리하려면 스레드 몇 개 필요?
  • Non-blocking이 항상 좋은가? (힌트: CPU 바운드 작업에서는 오히려 손해)

Unit 7.5 — 오버헤드와 File 객체

선수 지식: Unit 7.4

핵심 개념

오버헤드:

  • 특정 기능을 수행하는 데 드는 간접적 시간/메모리/자원
  • 예: 10초 짜리 작업이 20초 걸리면 오버헤드는 10초

File 객체 (전통 IO):

File file = new File(path, fileName);
file.createNewFile();        // 파일 생성
file.getAbsolutePath();      // 절대경로
file.getCanonicalPath();     // 정규화된 경로
file.getName();              // 파일명
file.getParent();            // 부모 디렉터리

자기 점검

  • "디렉터리는 자동 생성되지 않는다"는 사실이 실무에서 자주 발생시키는 버그는?

📚 Phase 8 — Stream 실전 (InputStream / OutputStream / Reader / Writer)

목표: 바이트 스트림과 문자 스트림의 차이를 손에 익히고 한글 처리까지 자유롭게 한다.

Unit 8.1 — System.in (그리고 한글이 안 되는 이유)

선수 지식: Phase 7

핵심 개념

  • System.in = 표준 입력 스트림 (보통 키보드)
  • InputStream 타입
  • read()1바이트씩 읽음

한글이 안 되는 이유:

  • 영어 1글자 = 1byte → OK
  • 한글 1글자 = 2~3byte (UTF-8) → read() 한 번으로는 깨짐

자기 점검

  • 한글을 제대로 읽으려면 어떤 클래스를 써야 하는가? (힌트: Reader)

Unit 8.2 — FileInputStream

선수 지식: Unit 8.1

핵심 개념

  • InputStream을 상속한 파일 전용 바이트 스트림
  • read(): 1바이트 읽기 (반환은 int)
  • 파일 끝(EOF) 시 -1 반환
  • 사용 후 반드시 close() (또는 try-with-resources)
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.3 — byte[] 배열로 효율적 읽기

선수 지식: Unit 8.2

핵심 개념

  • 1바이트씩 읽으면 너무 느림 → 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.4 — FileOutputStream

선수 지식: 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'
}

바이트가 영어로 보이는 이유:

  • 파일에 저장된 건 숫자(바이트)
  • 텍스트 에디터가 인코딩을 해석해서 문자로 보여줌
  • 65 → 'A' (ASCII/UTF-8 매핑)

자기 점검

  • 한글을 write(byte) 로 1바이트씩 쓰면 왜 깨지는가?

Unit 8.5 — 한글 처리 (FileReader, InputStreamReader)

선수 지식: Unit 8.4

핵심 개념

  • Reader/Writer: 문자(char) 단위로 처리하는 스트림
  • 인코딩을 알아서 해석 → 한글, 일본어 등 다국어 OK

두 가지 방법:

// 방법 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);
}

자기 점검

  • FileReaderInputStreamReader 의 차이는?
  • 어떤 경우에 InputStreamReader 방식이 필요한가? (힌트: 인코딩 명시)

Unit 8.6 — FileWriter (한글 쓰기)

선수 지식: 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("안녕하세요");
}

자기 점검

  • FileWriterFileOutputStream 을 한글 데이터에 각각 쓰면 결과는?

📚 Phase 9 — I/O 강화 (Buffered / Data / Serialization)

목표: 기본 스트림에 보조 스트림을 입혀 성능·편의성·구조화를 모두 잡는다.

Unit 9.1 — try-with-resources (자동 close)

선수 지식: 1주차 Phase 7

핵심 개념

  • Java 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가 더 안전한 이유는?

Unit 9.2 — BufferedInputStream / BufferedOutputStream

선수 지식: Phase 8

핵심 개념

  • 기본 스트림은 1바이트마다 OS 호출 → 느림
  • Buffered 스트림은 내부 버퍼(기본 8KB) 에 모아서 한 번에 처리
  • I/O 호출 횟수가 줄어들어 성능 대폭 향상
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);
}

자기 점검

  • Buffered 없이 1MB 파일을 1바이트씩 복사하면 OS 호출이 몇 번? (대략)
  • BufferedReader도 같은 원리인가?

Unit 9.3 — DataInputStream / DataOutputStream

선수 지식: 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();

자기 점검

  • 텍스트 저장 vs 이진 저장의 장단점은?

Unit 9.4 — Serialization (직렬화)

선수 지식: 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.5 — serialVersionUID

선수 지식: Unit 9.4

핵심 개념

  • 직렬화된 클래스의 버전 식별자
  • 명시 안 하면 컴파일러가 자동 생성 (클래스 구조 기반)
  • 클래스 변경 시 자동 생성 UID가 바뀌면 → 역직렬화 시 InvalidClassException
public class MyClass implements Serializable {
    private static final long serialVersionUID = 1L;
    // ...
}

자기 점검

  • 운영 중인 시스템에서 serialVersionUID 명시 누락이 어떤 사고로 이어지는가?
  • IDE가 serialVersionUID 누락에 경고를 띄우는 이유는?

📚 Phase 10 — 함수형 프로그래밍 (람다와 스트림)

목표: 함수를 값으로 다루는 새 패러다임으로 컬렉션 처리를 선언적으로 한다.

Unit 10.1 — 함수형 인터페이스(Functional Interface)

선수 지식: Phase 4

핵심 개념

  • 추상 메서드가 정확히 1개 인 인터페이스
  • @FunctionalInterface 어노테이션 (강제 검증)
  • 람다식으로 인스턴스화 가능

자바 표준 함수형 인터페이스:

인터페이스시그니처용도
Function<T, R>R apply(T)변환
Predicate<T>boolean test(T)조건
Consumer<T>void accept(T)소비
Supplier<T>T get()공급

자기 점검

  • 추상 메서드가 2개인 인터페이스에 람다를 쓸 수 있는가?
  • Runnable, Comparator, Comparable 도 함수형 인터페이스인가?

Unit 10.2 — 람다 표현식

선수 지식: 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;
  • 변수 캡처: 람다 안에서 외부 지역변수 사용 가능 (단, 사실상 final)

자기 점검

  • 익명 클래스에 비해 람다가 가지는 단점은? (힌트: this 의미, 디버깅)

Unit 10.3 — 스트림 (I/O 스트림과 다름)

선수 지식: Unit 10.2, Phase 2

핵심 개념

  • 컬렉션·배열에 동일 연산을 일관성 있게 처리
  • I/O 스트림과는 완전히 다른 개념 (이름만 같음)
  • 한 번 사용한 스트림은 재사용 불가
  • 중간 연산 + 최종 연산 으로 구성

중간 연산 (Stream 반환):

  • filter, map, sorted, distinct, limit, skip

최종 연산 (값 또는 void 반환):

  • forEach, collect, count, reduce, anyMatch
sList.stream()
    .filter(s -> s.length() > 3)
    .map(String::toUpperCase)
    .sorted()
    .forEach(System.out::println);

자기 점검

  • 스트림은 왜 한 번만 사용 가능한가?
  • 중간 연산만 호출하고 최종 연산을 안 하면 어떻게 되는가? (힌트: 지연 평가)

Unit 10.4 — 스트림 실전 패턴

선수 지식: 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+)의 차이는?

🎓 종합 자기 점검 (3주차 졸업 시험)

Pass by Value

  1. C 포인터의 *p&a 의 의미를 설명하라
  2. 자바가 "Pass by reference 없음"이라는 말의 정확한 의미는?
  3. arg2 = arg1 가 호출자에 영향을 안 주는 이유를 스택 프레임으로 설명하라

컬렉션 (전체 지도)

  1. List/Set/Queue/Map의 차이를 한 문장씩 정리하라
  2. ArrayList vs LinkedList vs Vector 선택 기준은?
  3. HashSet/TreeSet/LinkedHashSet 차이는?
  4. HashMap/LinkedHashMap/TreeMap/HashTable/ConcurrentHashMap 비교
  5. Queue 메서드 페어 (offer/add, peek/element, poll/remove) 차이는?

해시

  1. 해시 함수의 좋은 조건 3가지는?
  2. 해시 충돌이 불가피한 이유는?
  3. 체이닝과 오픈 어드레싱의 차이는?
  4. Java 8 HashMap이 트리로 변환되는 조건은?

추상클래스 vs 인터페이스

  1. 인터페이스 필드가 자동으로 public static final 인 이유는?
  2. Java 8의 default 메서드가 도입된 이유는?
  3. 추상클래스와 인터페이스 중 무엇을 선택할지의 기준은?

제네릭과 와일드카드

  1. List<String>List<Object> 가 아닌 이유는?
  2. PECS 원칙을 한 문장으로 설명하라
  3. Box<? extends Fruit> 에 add를 못 하는 이유는?

Comparable & Comparator

  1. Comparable과 Comparator의 차이는?
  2. compareTo 의 반환값 규칙은?
  3. 같은 클래스를 여러 기준으로 정렬해야 한다면 무엇을 쓰는가?

I/O

  1. JVM 기준 Input과 Output의 정의는?
  2. Stream과 Channel의 핵심 차이 3가지는?
  3. Blocking과 Non-blocking의 차이를 동시 접속 1만 명 시나리오로 설명하라
  4. 한글이 read() 1바이트로는 안 읽히는 이유는?

보조 스트림

  1. BufferedStream이 성능을 향상시키는 메커니즘은?
  2. DataStream이 텍스트 저장보다 유리한 이유는?
  3. serialVersionUID 를 명시해야 하는 이유는?

함수형

  1. 함수형 인터페이스의 정의는?
  2. 스트림이 한 번만 사용 가능한 이유는?
  3. 중간 연산과 최종 연산의 차이는?

📌 학습 운영 팁

9-섹션 마스터 프롬프트로 깊이 파야 할 Unit

반드시 깊이 파기:

  • Unit 1.3 — 자바의 Call by Value 한 가지 (면접 단골)
  • Unit 3.3 ~ 3.4 — 해시 충돌 해결 (HashMap 면접 단골)
  • Unit 5.5 — PECS 원칙 (제네릭 마스터의 핵심)
  • Unit 7.4 — Blocking vs Non-blocking (서버 사이드 필수)
  • Unit 10.3 — 스트림 (자바 8 이후 필수 도구)

Phase별 진도 체크리스트

[ ] 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문항 통과

1·2·3주차 흐름 정리

  • 1주차: OOP·JVM·GC·컬렉션·I/O 개론
  • 2주차: JVM 내부·바이트코드·G1 GC·컬렉션 내부·Reflection·Iterator
  • 3주차 (지금): 컬렉션 전체 지도·해시·제네릭·와일드카드·비교·I/O 깊이·함수형
profile
Software Developer

0개의 댓글