기본 패턴
String name = "Kim";
// 값 비교는 반드시 equals()
String a = new String("hello");
String b = new String("hello");
System.out.println(a.equals(b)); // true
System.out.println(a == b); // false — 참조가 다름
// 자주 쓰는 메서드
System.out.println(name.length()); // 3
System.out.println(name.toUpperCase()); // "KIM"
System.out.println(" hello ".strip()); // "hello"
System.out.println(name.contains("im")); // true설명
String이 불변인 이유와 그 결과
Java의 String은 불변(immutable) 객체입니다. 한 번 생성된 String의 내용은 변경할 수 없습니다. s = s + "x" 처럼 보이는 수정 코드는 실제로는 새 String 객체를 만들어 참조를 교체합니다.
불변으로 설계된 이유는 여러 스레드가 같은 문자열을 안전하게 공유할 수 있고, String Pool(인터닝)을 통한 메모리 최적화가 가능하기 때문입니다. 단점은 수정할 때마다 새 객체가 생겨 메모리 압박이 생길 수 있다는 것입니다.
String s = "hello";
s = s + " world"; // s는 새 객체 "hello world"를 가리킴
// 원래 "hello" 객체는 GC 대상
// 다른 변수에서 참조했다면 영향 없음
String original = "hello";
String modified = original + " world";
System.out.println(original); // "hello" — 변화 없음== vs equals() — 참조 비교 vs 값 비교
==는 두 참조가 같은 객체를 가리키는지 확인합니다. 값이 같아도 객체가 다르면 false입니다. equals()는 내용이 같은지 비교합니다.
String Pool 때문에 리터럴 문자열은 ==가 true일 수 있지만, new String()으로 만든 경우나 런타임에 생성된 문자열은 그렇지 않습니다. 문자열 비교는 항상 equals()를 사용하는 것이 안전합니다.
// 리터럴 — String Pool에서 같은 객체 재사용
String a = "hello";
String b = "hello";
System.out.println(a == b); // true (Pool에서 같은 객체)
System.out.println(a.equals(b)); // true
// new — 항상 새 객체
String c = new String("hello");
System.out.println(a == c); // false (다른 객체)
System.out.println(a.equals(c)); // true (값은 같음)
// 런타임 생성 — Pool 보장 없음
String d = "hel" + "lo"; // 컴파일 타임 상수 — Pool 사용
String e = "hel";
String f = e + "lo"; // 런타임 연결 — 새 객체
System.out.println(a == f); // falseStringBuilder — 반복 연결의 비용 문제
String이 불변이므로 +로 반복 연결하면 매번 새 객체가 생성됩니다. 루프 안에서 n번 연결하면 중간 객체가 O(n)개 생깁니다. StringBuilder는 내부 가변 버퍼에 문자를 누적하므로 최종 결과 객체 하나만 생성됩니다.
단순 + 연결 몇 번은 컴파일러가 StringBuilder로 자동 최적화하지만, 루프 안에서는 최적화가 되지 않으므로 직접 써야 합니다.
// ❌ 루프 안 + 연결 — 매번 새 String 생성
String result = "";
for (String word : words) {
result += word + " "; // 루프마다 새 객체 2개
}
// ✅ StringBuilder — 버퍼에 누적, 마지막에 한 번 변환
StringBuilder sb = new StringBuilder();
for (String word : words) {
sb.append(word).append(" ");
}
String result = sb.toString();빠른 정리
| 상황 | 선택 |
|---|---|
| 문자열 값 비교 | equals() (== 금지) |
| 대소문자 무시 비교 | equalsIgnoreCase() |
| 반복 문자열 연결 (루프) | StringBuilder |
| 단순 연결 몇 번 | + (컴파일러 최적화) |
| 앞뒤 공백 제거 | strip() (Unicode 지원) |
| 부분 문자열 | substring(start, end) |
| 문자열 → 숫자 | Integer.parseInt(s) |
주의할 점
null 체크 없이 equals()를 호출하면 NullPointerException이 발생합니다. 상수나 리터럴을 앞에 두거나 Objects.equals()를 사용하세요.
String input = null;
// ❌ null인 변수에 equals() 호출 — NullPointerException
if (input.equals("hello")) { ... }
// ✅ 리터럴을 앞에 두면 NPE 없음
if ("hello".equals(input)) { ... } // input이 null이면 그냥 false
// ✅ Objects.equals() — 양쪽 null 안전
if (Objects.equals(input, "hello")) { ... }참고 링크
2 sources