Java: 문자열 리터럴 vs new String() 객체 생성 차이
1. 개요
Java에서 문자열은 두 가지 방식으로 생성할 수 있다:
String s1 = "hello"; // 리터럴 방식
String s2 = new String("hello"); // new 키워드 방식
두 방식은 겉보기에는 동일한 문자열 값을 가지지만, 내부 메모리 구조, 생성 방식, 비교 결과, 성능 등에서 중요한 차이를 가진다.
2. 메모리 구조 차이
2.1 리터럴 방식 (String s = "hello"
)
- 문자열 리터럴은 String Constant Pool이라는 특별한 메모리 영역에 저장된다.
- 동일한 리터럴이 여러 번 사용되더라도 중복 없이 하나의 객체로 공유됨.
- JVM이 자동 최적화해 메모리 사용이 매우 효율적이다.
예시
String a = "hello";
String b = "hello";
System.out.println(a == b); // true (같은 객체 참조)
System.out.println(a.equals(b)); // true (내용도 동일)
→ 같은 상수 풀 객체를 참조하고 있기 때문에 a == b
는 true
.
2.2 new 연산자 방식 (String s = new String("hello")
)
new String()
은 무조건 Heap 메모리에 새로운 객체를 생성한다.- 내부적으로 리터럴
"hello"
를 참조하더라도, 그 값을 복제한 별도 인스턴스가 생성됨.
예시
String a = "hello";
String b = new String("hello");
System.out.println(a == b); // false (다른 참조)
System.out.println(a.equals(b)); // true (내용은 같음)
→ ==
결과는 false
, .equals()
는 true
3. 객체 비교 방식
3.1 ==
연산자 (참조 비교)
- 두 객체의 참조 주소값을 비교함
- 리터럴 방식끼리는 상수 풀 공유로
true
일 수 있음 new
로 생성한 객체는 항상false
3.2 .equals()
메서드 (값 비교)
- 두 문자열의 내용이 같은지 비교함
- 방식과 무관하게, 내용이 같으면 항상
true
String s1 = "test";
String s2 = new String("test");
System.out.println(s1 == s2); // false
System.out.println(s1.equals(s2)); // true
4. 성능 및 메모리 효율
항목 | 리터럴 방식 ( "hello" ) | new 방식 ( new String("hello") ) |
---|---|---|
메모리 위치 | Constant Pool (Method Area) | Heap 영역 |
객체 중복 여부 | 없음 (공유됨) | 항상 새 객체 생성 |
비교 결과 | true (동일 참조) | false (서로 다른 참조) |
결과 | true | true |
메모리 효율 | 높음 | 낮음 |
성능 | 빠름 | 느림 (GC 대상 증가) |
GC 부담 | 낮음 | 높음 |
사용 권장 여부 | ✅ 일반적으로 권장 | ❌ 특별한 경우에만 사용 |
5. 실제 사용 시 차이
5.1 리터럴 방식 – 일반적으로 권장
- 정적 값, 설정 상수, 조건 분기 등에 적합
- 성능과 메모리 효율이 우수함
if (userRole.equals("ADMIN")) {
// 권한 체크
}
5.2 new 방식 – 특수 목적용
- 동일한 문자열이더라도 서로 다른 객체를 명시적으로 생성할 때
- 예: 문자열 캐시 우회, 디버깅용 비교, 참조를 강제로 분리해야 할 경우
- 필요하다면
.intern()
메서드를 사용해 수동으로 상수 풀 등록 가능
String s = new String("hello").intern(); // Constant Pool 등록
6. 정리: 리터럴 vs new String()
비교 항목 | 리터럴 방식 ( "hello" ) | new 방식 ( new String("hello") ) |
---|---|---|
저장 위치 | String Constant Pool | Heap 메모리 |
객체 공유 여부 | ✅ 공유됨 (중복 방지) | ❌ 매번 새 객체 생성 |
비교 결과 | true | false |
결과 | true | true |
성능 | 빠름 | 느림 |
메모리 효율 | 높음 | 낮음 |
GC 부담 | 낮음 | 높음 |
사용 추천 여부 | ✅ 일반적 상황에서 적극 권장 | ⚠️ 특별한 목적이 없으면 지양 |
7. 결론
- 문자열은 리터럴 방식으로 생성하는 것이 가장 효율적이다.
new String()
방식은 불필요한 Heap 객체 생성으로 인해 성능과 메모리 측면에서 불리하다.- 문자열 비교 시에는 항상
==
이 아닌.equals()
를 사용할 것. - 고급 최적화가 필요한 경우에는
.intern()
으로 Constant Pool을 수동 활용할 수 있다.
이 정리는 Java
메모리 구조, 성능 최적화, 참조와 객체 비교에 대해 정확한 이해를 돕기 위한 위키 스타일
자바에서 문자열은 두 가지 방법으로 생성할 수 있다.
String str = "hello";와 String str = new String("hello"); 두 가지 방식은 같아 보이지만, 작동 방식, 메모리 구조, 성능, 객체 비교 등 여러 측면에서 차이가 난다.
String s1 = "hello"; // 리터럴 방식
String s2 = new String("hello"); // new 연산자 사용
두 방식은 겉보기에는 같은 값을 가지지만, 메모리 처리, 비교 방법, 성능 등의 측면에서 분명한 차이가 존재한다.
1. 메모리 구조 차이
1.1 리터럴 방식: String s1 = "hello";
문자열 리터럴은 String Constant Pool에 저장된다.
JVM은 동일한 문자열 리터럴이 여러 번 등장하더라도 중복을 제거하여 하나의 객체만 저장한다.
메모리 효율이 높고, 비교 연산 시 빠르다.
📌 예시
String a = "hello";
String b = "hello";
System.out.println(a == b); // true (같은 상수 풀 객체)
1.2 new 연산자 방식: String s2 = new String("hello");
new 키워드를 사용하면 Heap에 새로운 객체가 강제로 생성된다.
비록 내부적으로 "hello"라는 리터럴이 사용되더라도, Constant Pool의 객체를 복제한 별도 인스턴스가 Heap에 만들어진다.
따라서 리터럴과는 다른 참조값을 가진다.
📌 예시
String a = "hello";
String b = new String("hello");
System.out.println(a == b); // false (주소 다름)
System.out.println(a.equals(b)); // true (값 같음)
2. 객체 비교 (== vs equals())
2.1 == (참조 비교)
리터럴 방식은 JVM이 Constant Pool을 공유하므로 == 결과가 true일 수 있다.
new 키워드는 항상 새로운 객체를 생성하므로 == 비교 시 false가 된다.
2.2 .equals() (내용 비교)
두 방식 모두 문자열 내용을 같게 만들면 .equals()는 항상 true를 반환한다.
문자열 비교에서는 항상 .equals()를 사용하는 것이 안전하다.
3. 성능 및 메모리 효율
3.1 리터럴 방식
- 런타임 시 상수 풀을 이용하므로 메모리 사용이 효율적이다.
- JVM이 자동으로 중복 문자열을 제거해주므로 불필요한 객체 생성을 방지할 수 있다.
- 가장 선호되는 방식.
3.2 new 방식
- new String()을 사용할 경우 매번 객체가 새로 만들어지므로 메모리 낭비 가능성이 있다.
- GC(Garbage Collector) 부담 증가 → 성능 저하로 이어질 수 있음.
- 특별한 이유(예: 강제로 다른 참조를 만들어야 할 필요)가 없다면 지양하는 것이 좋다
4. 실제 사용 시 차이점
4.1 리터럴 방식의 사용 예
- 상수 정의, 비교가 필요한 경우
- 조건 분기, 설정 값 체크 등에서 성능 이점 있음.
if (userRole.equals("ADMIN")) { ... }
4.2 new 방식의 사용 예
- 객체 생성을 명시적으로 컨트롤해야 하는 특수한 경우
- 외부 API나 라이브러리에서 intern() 메서드를 사용하여 상수 풀로 다시 보낼 수도 있음
String s = new String("hello").intern(); // 풀로 보냄
5. 정리
리터럴 방식 ( | new 방식 ( | |
---|---|---|
메모리 위치 | Constant Pool (Method Area) | Heap 영역 |
객체 중복 여부 | 없음 (공유됨) | 항상 새 객체 생성 |
| true (같은 참조) | false (다른 객체) |
| true | true |
메모리 효율 | 높음 | 낮음 |
성능 | 빠름 | 느림 |
GC 부담 | 낮음 | 높음 |
사용 권장 여부 | 일반적으로 권장 | 특별한 경우에만 사용 |
6. 결론
- 문자열을 선언할 때는 특별한 이유가 없다면 "hello"와 같은 리터럴 방식을 사용하는 것이 가장 효율적이다.
- new String()은 불필요한 객체 생성을 유발하며, 메모리와 성능 측면에서 손해를 볼 수 있다.
- 문자열 비교 시에는 항상 ==가 아닌 .equals()를 사용할 것.
- 최적화를 원할 경우 intern()을 활용해 상수 풀을 직접 사용할 수도 있다.