공공부하자개발 · 영어 학습 노트
자바
중급객체지향과 코어 라이브러리0/9 완료
  • 01상속, 다형성, 오버라이딩
  • 02추상 클래스 vs 인터페이스
  • 03예외 처리
  • 04java.lang 심화
  • 05컬렉션 프레임워크 딥다이브
  • 06메서드 활용 패턴 (중급)
  • 07Object 메서드와 비교
  • 08java.time 실무 날짜 계산
  • 09HTTP 와 JSON 기초
사이트 소개개인정보처리방침연락처
© 2026 공부하자
홈 › 중급 › 04 / 9

java.lang 심화

String, StringBuilder, Wrapper
섹션 7진행 0 / 9
1왜 배우는가2핵심 원리3코드 예제4응용 변형 예제5자주 하는 실수 (Tip)6연습 문제7정리‹ 이전다음 ›

3. 코드 예제

예제 1: String Pool과 ==, equals, intern

리터럴, new, 런타임 생성, intern 각각의 참조 동일성을 확인한다.

java
public class Main {
    public static void main(String[] args) {
        String a = "ACTIVE";
        String b = "ACTIVE";
        String c = new String("ACTIVE");
        String d = "ACT" + "IVE";              // 컴파일 타임 상수
        String prefix = "ACT";
        String e = prefix + "IVE";             // 런타임 연결
        String f = e.intern();

        System.out.println("a == b : " + (a == b));           // 출력: a == b : true
        System.out.println("a == c : " + (a == c));           // 출력: a == c : false
        System.out.println("a == d : " + (a == d));           // 출력: a == d : true
        System.out.println("a == e : " + (a == e));           // 출력: a == e : false
        System.out.println("a == f : " + (a == f));           // 출력: a == f : true
        System.out.println("a.equals(c) : " + a.equals(c));   // 출력: a.equals(c) : true

        // 실무 함정: 외부에서 읽어 온 값과의 비교
        String fromDb = new StringBuilder("ACTI").append("VE").toString();
        System.out.println("== 비교: " + (fromDb == "ACTIVE"));          // 출력: == 비교: false  (버그)
        System.out.println("equals 비교: " + fromDb.equals("ACTIVE"));   // 출력: equals 비교: true
    }
}

예제 2: 문자열 연결 성능 벤치마크

루프 안 +, StringBuilder, 초기 용량 지정 StringBuilder의 시간을 비교한다.

java
public class Main {
    static final int N = 50_000;

    static String withPlus() {
        String s = "";
        for (int i = 0; i < N; i++) s += i;          // O(n^2)
        return s;
    }

    static String withBuilder() {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < N; i++) sb.append(i);    // O(n)
        return sb.toString();
    }

    static String withSizedBuilder() {
        StringBuilder sb = new StringBuilder(N * 5); // 재할당 없음
        for (int i = 0; i < N; i++) sb.append(i);
        return sb.toString();
    }

    static long timeMs(Runnable r) {
        long t0 = System.nanoTime();
        r.run();
        return (System.nanoTime() - t0) / 1_000_000;
    }

    public static void main(String[] args) {
        System.out.println("String +           : " + timeMs(Main::withPlus) + "ms");
        System.out.println("StringBuilder      : " + timeMs(Main::withBuilder) + "ms");
        System.out.println("StringBuilder(cap) : " + timeMs(Main::withSizedBuilder) + "ms");
        // 출력 (환경에 따라 다름):
        // String +           : 3450ms
        // StringBuilder      : 30ms
        // StringBuilder(cap) : 25ms
    }
}

예제 3: 로그 파싱 — split, strip, 텍스트 블록, formatted

여러 줄 로그를 텍스트 블록으로 정의하고 lines(), split, strip으로 파싱한 뒤 formatted로 보고서를 만든다.

java
import java.util.*;

public class Main {
    public static void main(String[] args) {
        String log = """
            2026-09-08 10:00:01 | INFO  | order=O-1 | amount=30000
            2026-09-08 10:00:05 | ERROR | order=O-2 | amount=15000
            2026-09-08 10:00:09 | INFO  | order=O-3 | amount=99000
            """;

        Map<String, Integer> countByLevel = new TreeMap<>();
        long total = 0;

        for (String line : log.lines().toList()) {
            String[] parts = line.split("\\|");                 // 정규식이므로 | 이스케이프
            String level = parts[1].strip();
            long amount = Long.parseLong(parts[3].strip().split("=")[1]);
            countByLevel.merge(level, 1, Integer::sum);
            if (level.equals("INFO")) total += amount;
        }

        System.out.println("레벨별 건수: " + countByLevel);
        System.out.println("정상 주문 합계: %,d원".formatted(total));
        System.out.println("=".repeat(30));
        // 출력:
        // 레벨별 건수: {ERROR=1, INFO=2}
        // 정상 주문 합계: 129,000원
        // ==============================
    }
}

예제 4: Integer 캐시 함정과 null 언박싱

==가 127까지만 되는 이유와 Map.get 결과를 int에 대입할 때의 NPE를 재현한다.

java
import java.util.HashMap;
import java.util.Map;

public class Main {
    public static void main(String[] args) {
        Integer a = 127, b = 127;
        Integer c = 128, d = 128;
        System.out.println("127 == 127 : " + (a == b));         // 출력: 127 == 127 : true
        System.out.println("128 == 128 : " + (c == d));         // 출력: 128 == 128 : false
        System.out.println("128 equals : " + c.equals(d));      // 출력: 128 equals : true
        System.out.println("언박싱 비교 : " + (c.intValue() == d));   // 출력: 언박싱 비교 : true

        Map<String, Integer> stock = new HashMap<>();
        stock.put("SKU-1", 10);

        try {
            int qty = stock.get("SKU-999");   // null → 언박싱 → NPE
            System.out.println(qty);
        } catch (NullPointerException e) {
            System.out.println("NPE 발생: 없는 키를 int로 언박싱");
            // 출력: NPE 발생: 없는 키를 int로 언박싱
        }

        int safe = stock.getOrDefault("SKU-999", 0);
        System.out.println("안전한 조회: " + safe);   // 출력: 안전한 조회: 0
    }
}

예제 5: 박싱 비용과 parseInt/valueOf

루프에서 Wrapper 누적과 원시 타입 누적의 시간을 비교하고, parseInt/valueOf의 반환 타입 차이를 확인한다.

java
public class Main {
    public static void main(String[] args) {
        final int N = 20_000_000;

        long t0 = System.nanoTime();
        Long boxedSum = 0L;
        for (int i = 0; i < N; i++) boxedSum += i;        // 매 반복 Long 객체 생성
        long boxedMs = (System.nanoTime() - t0) / 1_000_000;

        t0 = System.nanoTime();
        long primitiveSum = 0L;
        for (int i = 0; i < N; i++) primitiveSum += i;
        long primitiveMs = (System.nanoTime() - t0) / 1_000_000;

        System.out.println("Long 누적 : " + boxedMs + "ms");
        System.out.println("long 누적 : " + primitiveMs + "ms");
        // 출력 (환경에 따라 다름):
        // Long 누적 : 500ms
        // long 누적 : 330ms

        int p = Integer.parseInt("42");          // int
        Integer v = Integer.valueOf("42");       // Integer (캐시 범위 → 재사용)
        System.out.println(p + v);               // 출력: 84
        System.out.println(v == Integer.valueOf(42));   // 출력: true (캐시)

        try {
            Integer.parseInt("42원");
        } catch (NumberFormatException e) {
            System.out.println("파싱 실패: " + e.getMessage());
            // 출력: 파싱 실패: For input string: "42원"
        }
    }
}

예제 직접 실행

아래 폴더를 JDK 21 로 컴파일하고 실행합니다.

cd java-src\intermediate\04_java_lang
javac -encoding UTF-8 *.java && java Main
코드 예제
  • 예제 1: String Pool과 ==, equals, intern
  • 예제 2: 문자열 연결 성능 벤치마크
  • 예제 3: 로그 파싱 — split, strip, 텍스트 블록, formatted
  • 예제 4: Integer 캐시 함정과 null 언박싱
  • 예제 5: 박싱 비용과 parseInt/valueOf
이전 섹션2 핵심 원리3 / 7다음 섹션4 응용 변형 예제