Java String 완벽 정리: intern(), 문자열 덧셈, 최적화까지
Java에서 String은 가장 자주 사용되면서도 성능에 큰 영향을 줄 수 있는 객체입니다. 이번 글에서는 String의 중요한 개념들과 실제 개발 시 주의할 점을 디테일하게 정리해보겠습니다.
🔹 1. String은 불변(Immutable) 객체다
- String 객체는 생성 이후 변경이 불가능합니다.
- 새로운 문자열 조작이 발생할 때마다 새로운 객체가 생성됩니다.
String a = "hello";
a += " world"; // "hello world"라는 새로운 String 객체가 생성됨
🔹 2. 문자열 리터럴은 String Constant Pool에 저장된다
- "hello" 와 같은 리터럴은 JVM의 String Constant Pool이라는 특별한 메모리 공간에 저장됨
- 동일한 리터럴을 여러 번 사용하더라도 같은 주소 참조를 사용하여 메모리 절약
String a = "test";
String b = "test";
System.out.println(a == b); // true
🔹 3. new String()은 heap에 객체를 생성한다
String a = new String("hello");
String b = "hello";
System.out.println(a == b); // false
System.out.println(a.equals(b)); // true
- a == b는 false → 서로 다른 객체 참조
- .equals()는 값이 같으므로 true
🔹 4. intern() 메소드의 개념과 원리
String.intern()은 다음과 같은 역할을 합니다:
| 동작 조건 | 결과 |
| 상수 풀에 동일한 문자열 존재 O | 그 참조를 반환 |
| 상수 풀에 동일한 문자열 존재 X | 현재 문자열을 상수 풀에 추가 후 그 참조 반환 |
💡 도식
String a = new String("hello");
String b = "hello";
a → heap: "hello"
b → constant pool: "hello"
a.intern() → constant pool의 "hello" 반환
📌 예제
String a = new String("test");
String b = "test";
System.out.println(a == b); // false
System.out.println(a.intern() == b); // true
⚠️ 5. intern() 사용 시 주의점
- 너무 많은 문자열을 intern하면 메모리 (Metaspace) 부담 증가 → OutOfMemoryError 가능
- intern()은 비용이 비싼 연산이므로 빈번하게 사용하지 말 것
- == 비교를 위해 남용하는 건 권장되지 않음 → equals()가 안전
🔹 6. 문자열 + 연산 (+)의 컴파일 동작 방식
✅ 일반 문자열 덧셈
String name = "Alice";
String greeting = "Hello, " + name;
→ JDK 5 이상에서는 컴파일 시 다음처럼 자동 변환됨:
String greeting = new StringBuilder()
.append("Hello, ")
.append(name)
.toString();
→ 성능 최적화되어 따로 걱정 안 해도 됨
❌ 반복문 내부에서 +는 비효율적
String result = "";
for (int i = 0; i < 1000; i++) {
result += i;
}
→ 위 코드는 반복할 때마다 StringBuilder 객체가 매번 생성되고 toString()이 호출됨
→ 결과적으로 성능 저하 및 GC 부하
✅ 권장 방법: StringBuilder 사용
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++) {
sb.append(i);
}
String result = sb.toString();
🔍 번외: 바이트 전송을 위한 getBytes()
- IO 통신 시 문자열을 바이트로 변환할 때 사용
- 예: 파일 저장, 네트워크 전송 시 필수
String msg = "Hello";
byte[] bytes = msg.getBytes(StandardCharsets.UTF_8);
🔍 String Pool에 저장되는 구조는?
JVM은 .class 파일 로딩 시 리터럴들을 Constant Pool 영역에 올려두고,
해당 리터럴이 반복 사용될 경우 기존 객체를 재사용합니다.
→ 이는 메모리 절약과 비교 속도 향상(==)에 도움
String a = "data";
String b = "da" + "ta"; // 컴파일 시점에 "data"로 합쳐짐
System.out.println(a == b); // true
🔚 결론 요약
| 항목 | 주의/특징 | 권장 사항 |
| String | 불변 객체 | 수정 시 새 객체 생성 |
| intern() | 상수 풀 참조 반환 | 자주 사용 시 메모리 문제 |
| + 연산 | 1회성은 자동 최적화 | 반복문에서는 StringBuilder 사용 |
| == vs equals() | 참조 vs 값 비교 | equals()가 안전 |
| String Pool | 리터럴 공유 저장소 | 동일 문자열 재사용됨 |
'기타 > 자바의 신' 카테고리의 다른 글
| (자바) 컬렉션 관련 정리 (자바의신 day9) (0) | 2025.07.06 |
|---|---|
| (자바) 내부 클래스란? (자바의 신 day7) (1) | 2025.07.01 |
| (자바) 자바의 신 day5 - 자바에서 업캐스팅과 다운캐스팅, equals와 hashCode, toString의 역할 및 오버라이딩 이유 (0) | 2025.06.25 |
| (자바) 자바의 신 day4 - 상속 기본 (생성자 규칙 + 오버라이딩) (0) | 2025.06.24 |
| (자바)자바의 신 day3 - 오늘의 학습 정리 (패키지, static, 참조/기본형 등) (0) | 2025.06.20 |