리터럴, new, 런타임 생성, intern 각각의 참조 동일성을 확인한다.
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
}
}루프 안 +, StringBuilder, 초기 용량 지정 StringBuilder의 시간을 비교한다.
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
}
}여러 줄 로그를 텍스트 블록으로 정의하고 lines(), split, strip으로 파싱한 뒤 formatted로 보고서를 만든다.
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원
// ==============================
}
}==가 127까지만 되는 이유와 Map.get 결과를 int에 대입할 때의 NPE를 재현한다.
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
}
}루프에서 Wrapper 누적과 원시 타입 누적의 시간을 비교하고, parseInt/valueOf의 반환 타입 차이를 확인한다.
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원"
}
}
}