String의 특징과 Wrapper Class

JV-ST-11calendar_today2026-01-02 18:15#Java #Level2

1. String과 상수 풀 (String Constant Pool)

String의 특징과 메모리 관리

자바의 문자열(String)은 불변(Immutable) 구조를 가집니다.

모든 문자열 리터럴은 **상수 풀(String Constant Pool)**에 저장됩니다.

동일한 문자열 리터럴("")을 생성하면, 풀 내의 같은 객체를 참조하게 만듭니다.

  • 이를 통해 메모리 낭비를 방지합니다.
  • 동일한 데이터에 대한 중복 공간 할당을 허용하지 않습니다.

String.intern() 메서드

Native 메서드로 구현되어 있습니다.

  • 자바 외부(C언어 등)에서 수행되므로 시스템 리소스와 밀접한 관계가 있습니다.

리터럴("")로 상수를 선언하면 내부적으로 intern()이 호출됩니다.

  • 예: String s1 = "Hello";

[동작 원리]

  1. 상수 풀에서 해당 리터럴 문자열을 검색합니다.
  2. 있으면: 이미 생성된 인스턴스를 반환합니다.
  3. 없으면: 새로 생성하여 풀에 추가한 뒤 반환합니다.

메모리 위치의 변화 (Java 7 전후)

Java 7 이후, 문자열 상수 풀의 위치가 변경되었습니다.

  • 기존: PermGen (Permanent Generation) 영역
  • 변경: Heap 영역

이로 인해 상수 풀에 저장된 문자열도 GC(Garbage Collection)의 대상이 됩니다.

  • 메모리 관리가 더욱 효율적으로 개선되었습니다.

상수 풀(Constant Pool)의 흐름과 생명주기

"A String Literal is always of type String"

문자열 상수는 시점에 따라 다른 공간을 거쳐갑니다.

  1. 컴파일 타임 (Compile Time)
    • .class 파일 내의 Constant Pool에 저장됩니다.
  2. 로딩 타임 (Loading Time)
    • 클래스 로드시 Runtime Constant Pool (Method Area)로 이동합니다.
  3. 실행 타임 (Runtime)
    • 실제 코드 실행 시 String Constant Pool (Heap Area)로 이동합니다.
    • 이때 intern() 메서드를 통해 중복 제거 및 캐싱이 이루어집니다.

2. StringBuilder: 가변(Mutable) 문자열

개요

**불변(Immutable)**인 String의 단점을 해결하기 위해 등장했습니다.

  • 문자열 연산이 잦을 때 발생하는 메모리 낭비를 줄여줍니다.

핵심 특징

가변(Mutable) 객체입니다.

값 변경 시 임시 객체를 생성하지 않습니다.

  • String은 연산 시마다 새로운 객체를 만들지만, StringBuilder는 인스턴스 메모리를 직접 수정합니다.

[주요 메서드]

  • append() : 문자열 뒤에 내용 추가
  • insert() : 특정 위치에 내용 삽입
  • delete() : 내용 삭제
  • reverse() : 문자열 뒤집기
  • toString() : String 객체로 변환

성능 및 사용 가이드

String과 비교했을 때 연산 속도 차이가 압도적입니다.

문자열 변경이 잦은 로직에서는 반드시 사용해야 합니다.

[참고] StringBuilder vs StringBuffer

  • StringBuilder: 동기화(Synchronization) 미지원 → 싱글 스레드 환경에서 가장 빠름.
  • StringBuffer: 동기화 지원 → 멀티 스레드 환경에서 안전(Thread-Safe)하지만 상대적으로 느림.

코드 예시

java
public class StringBuilderExample {
    public static void main(String[] args) {
        // 1. 객체 생성 (가변 공간 확보)
        StringBuilder sb = new StringBuilder("Java");

        // 2. append(): 뒤에 내용 추가 (메모리 주소는 그대로, 내용만 바뀝니다)
        sb.append(" Script"); 
        // 현재 상태: "Java Script"

        // 3. insert(): 특정 위치(4번 인덱스)에 내용 끼워 넣기
        sb.insert(4, "Object"); 
        // 현재 상태: "JavaObject Script"

        // 4. delete(): 범위 삭제 (4번부터 10번 앞까지)
        sb.delete(4, 10); 
        // 현재 상태: "Java Script"

        // 5. reverse(): 문자열 전체 뒤집기
        sb.reverse(); 
        // 현재 상태: "tpircS avaJ"

        // 6. toString(): 최종 결과를 불변인 String으로 변환
        String finalResult = sb.toString();
        
        System.out.println(finalResult);
    }
}

3. 래퍼 클래스 (Wrapper Class)

등장 배경

자바의 **기본 데이터 형식(Primitive Type)**은 클래스가 아닙니다.

  • 따라서 메서드를 가질 수 없습니다.
  • null 값을 가질 수 없습니다.
  • 제네릭(List<int> 불가)에서 사용할 수 없습니다.

이러한 한계를 해결하기 위해 기본 타입을 클래스로 감싼 것이 래퍼 클래스입니다.

종류

  • Byte, Short, Integer, Long
  • Float, Double
  • Character
  • Boolean

박싱과 언박싱 (Boxing & Unboxing)

박싱(Boxing)

  • 기본형 데이터를 래퍼 클래스 객체로 변환하는 것.
  • 오토 박싱(Auto-boxing): 자바 컴파일러가 자동으로 변환해주는 기능.
    • 예: Integer obj = 128; (내부적으로 new Integer(128) 처럼 동작)

언박싱(Unboxing)

  • 래퍼 클래스 객체에서 기본형 데이터 값을 꺼내는 것.
  • 예: int val = obj; (내부적으로 obj.intValue() 호출)

[주의] 값 비교 시

  • 래퍼 클래스는 객체이므로 == 연산자는 주소값을 비교합니다.
  • 값 자체를 비교하려면 .equals()를 사용해야 합니다.
  • (단, Integer의 경우 -128 ~ 127 범위는 캐싱되어 == 비교가 가능할 수 있으나, 안전하게 .equals() 사용을 권장합니다.)