String과 래퍼 클래스는 왜 객체를 생성하지 않고 사용할 수 있을까?
Java에서 String
과 래퍼 클래스(Integer
, Double
, Boolean
등)는 객체 재사용을 위한 특별한 설계 덕분에 new
키워드를 사용하지 않고도 쉽게 사용할 수 있다. 이는 String Pool과 오토박싱 & 캐싱 기법 덕분이다.
1. String Pool (문자열 상수 풀)
String Pool은 JVM의 힙 메모리에서 특별한 영역으로, 동일한 문자열 리터럴을 공유하여 메모리를 절약하는 기법이다.
String s1 = "hello";
String s2 = "hello";
System.out.println(s1 == s2); // true (같은 객체)
"hello"
는 String Pool에 저장되며, 동일한 문자열이 있으면 기존 객체를 재사용한다.- 하지만
new String("hello")
를 사용하면 힙(Heap) 영역에 새로운 객체를 생성한다.
String s3 = new String("hello");
System.out.println(s1 == s3); // false (다른 객체)
new String("hello")
는 String Pool을 사용하지 않으므로,s1
과 다른 객체가 된다.- 그러나
intern()
을 사용하면 String Pool에 추가하여 기존 객체를 재사용할 수 있다.
String s4 = new String("hello").intern();
System.out.println(s1 == s4); // true (String Pool을 참조)
2. 래퍼 클래스의 오토박싱(Auto-boxing)과 언박싱(Unboxing)
오토박싱(Auto-boxing)
자바에서는 기본형(Primitive type) 값을 래퍼 클래스 객체로 자동 변환하는 기능을 제공한다.
Integer num = 10; // 내부적으로 Integer.valueOf(10) 호출
➡ valueOf()
를 호출하여 Integer
객체로 변환됨.
언박싱(Unboxing)
래퍼 클래스 객체에서 기본형 값을 자동으로 추출하는 기능도 제공한다.
int n = num; // 내부적으로 num.intValue() 호출
➡ intValue()
를 호출하여 기본형(int)으로 변환됨.
3. 래퍼 클래스의 캐싱 (Wrapper Class Caching)
특정 범위의 값은 캐싱하여 객체를 재사용한다.
이로 인해 동일한 값을 가진 객체를 여러 번 생성하지 않고, 메모리를 절약할 수 있다.
Integer num1 = 100;
Integer num2 = 100;
System.out.println(num1 == num2); // true (같은 객체 재사용)
하지만 캐싱 범위를 벗어나면 새로운 객체를 생성한다.
Integer num3 = 200;
Integer num4 = 200;
System.out.println(num3 == num4); // false (다른 객체)
valueOf()
내부에서 -128 ~ 127 범위의 값은 캐싱되고, 그 외 값은 새로운 객체가 생성된다.
각 래퍼 클래스의 캐싱 범위
래퍼 클래스 | 캐싱 범위 |
---|---|
Integer | -128 ~ 127 |
Byte | -128 ~ 127 |
Short | -128 ~ 127 |
Character | 0 ~ 127 |
Boolean | true , false |
Long | -128 ~ 127 |
Double / Float | ❌ 캐싱 안 함 (항상 새로운 객체 생성) |
4. valueOf()
vs new Integer()
차이
Integer a = Integer.valueOf(100); // 캐싱된 객체 사용
Integer b = new Integer(100); // 새로운 객체 생성
System.out.println(a == b); // false
Integer.valueOf(100)
은 캐싱된 객체를 재사용하여 성능을 최적화한다.new Integer(100)
은 항상 새로운 객체를 생성하므로 비효율적이다.- Java 9 이후
new Integer(int)
생성자는 Deprecated(사용 비권장).
5. String과 래퍼 클래스의 차이점
구분 | String | 래퍼 클래스 (Integer , Double 등) |
---|---|---|
역할 | 문자열 저장 및 조작 | 기본형(Primitive type)을 객체로 감싸는 역할 |
불변성(Immutable) | ✅ 변경 불가능 | ✅ 변경 불가능 |
메모리 최적화 | String Pool 사용 (리터럴 재사용) | -128 ~ 127 범위 캐싱 |
기본형과 연결 | ❌ 없음 (기본형이 아님) | ✅ int ↔ Integer 자동 변환 (오토박싱/언박싱) |
객체 생성 방식 | "hello" → String Pool에서 재사용 |
Integer.valueOf(100) → 캐싱된 객체 사용 |
➡ String은 기본형과 관련이 없지만, 래퍼 클래스는 기본형 값을 객체로 변환하는 역할을 한다.
6. 결론
String
은 String Pool을 사용하여 동일한 문자열을 재사용한다.- 래퍼 클래스는
valueOf()
를 통해 특정 범위(-128 ~ 127)의 값을 캐싱하여 객체를 재사용한다. - 오토박싱(Auto-boxing)과 언박싱(Unboxing) 덕분에
new Integer(10)
을 사용하지 않고도Integer num = 10;
처럼 객체를 생성할 수 있다. - Double과 Float은 캐싱을 지원하지 않으며, 항상 새로운 객체가 생성된다.
- Java는 성능 최적화를 위해 객체 재사용 기법을 적극 활용하고 있다.
➡ 이러한 설계 덕분에 new
없이도 String
과 래퍼 클래스 객체를 사용할 수 있다!
'자바 > 객체지향 프로그래밍(OOP)' 카테고리의 다른 글
생성자와 객체생성에 대한 이해 (0) | 2025.01.27 |
---|---|
클래스란? (0) | 2025.01.26 |
추상 클래스 (0) | 2025.01.23 |
업캐스팅과 다운캐스팅 상태에서의 변수와 메서드 사용 가능 여부 (0) | 2025.01.23 |
인스턴스 변수 (0) | 2025.01.23 |