String 불변성과 StringBuilder

코딩Java문자열
읽는데 7분 소요
처음 쓰여진 날: 2026-05-19
마지막 수정일: 2026-05-19
조회수:
선수학습(1개)

요약

Java의 String은 한 번 만들면 내용을 바꿀 수 없는 불변(immutable) 객체입니다. 반복문에서 + 연산으로 문자열을 누적하면 매번 새 객체가 생기는 성능 함정을, 가변 버퍼인 StringBuilder의 append()로 어떻게 해결하는지 정리합니다.

핵심 정리

개념설명
String불변(immutable) 객체. 연결 시 새 객체가 생성됨
StringBuilder가변(mutable) 문자열 버퍼. 같은 객체에 누적
반복 + 연결String은 매 반복마다 새 객체, StringBuilder는 객체 1개 유지
주요 메서드append, insert, delete, reverse, toString

여기서 객체는 '메모리에 만들어진 데이터 덩어리'라고 생각하면 됩니다. 자세한 의미는 객체와 클래스에서 다룹니다.


String 불변성 심화

Java의 String불변(immutable) 객체입니다. 한 번 만들어진 문자열 객체의 내용은 절대 바뀌지 않습니다.

변수 s는 객체 그 자체가 아니라 '객체가 저장된 위치(주소)'를 들고 있는 종이쪽지입니다. 종이에 적힌 주소가 바뀔 수는 있지만, 그 주소가 가리키는 객체의 내용물 자체는 바뀌지 않습니다. 이 차이가 '불변'의 핵심입니다. str = str + "A" 처럼 보이는 코드도 실제로는 기존 객체를 수정하는 것이 아니라 새 문자열 객체를 만들어 참조만 바꿉니다. 변수 s가 가리키는 객체가 새 객체로 갈아끼워질 뿐, 옛 객체 자체의 내용이 바뀐 적은 없습니다. 이걸 '객체는 불변'이라고 합니다.

java
코드 하이라이팅 중...
단계메모리 상태참조
String s = "Hello";"Hello" 객체 1개s → "Hello"
s = s + " World";"Hello", "Hello World" 객체 2개s → "Hello World"
String 불변성 — Hello에서 Hello World로 변할 때 새 객체 생성
s + ' World'는 기존 객체를 수정하는 게 아니라 새 객체(주소 200)를 만들어 s의 참조만 갈아끼웁니다. 옛 객체(주소 100)는 가리키는 변수가 없으므로 가비지 컬렉션(GC, Java가 안 쓰는 객체를 자동으로 메모리에서 정리하는 기능) 대상이 됩니다.

원래의 "Hello" 객체는 그대로 남아 있지만, 더 이상 가리키는 참조가 없으면 Garbage Collector1가 정리합니다. 정확한 시점은 정해져 있지 않고, JVM이 메모리가 필요해질 때 알아서 회수합니다.

반복 연결의 비용

문자열을 반복해서 이어 붙이면, 매 반복마다 새로운 String 객체가 하나씩 생깁니다. 1000번 반복하면 약 1000개의 String 객체가 만들어지고, 매번 기존 문자열을 새 객체로 복사하므로 반복 횟수가 커질수록 처리 시간이 급격히 늘어납니다.

java
코드 하이라이팅 중...
반복내부 동작
1회차"" + "x""x" 객체 생성
2회차"x" + "x""xx" 객체 생성 (기존 "x"는 버려짐)
3회차"xx" + "x""xxx" 객체 생성
...반복마다 새 객체 누적
String += 반복의 누적 비용
매 반복마다 새 객체가 만들어지고 옛 객체는 가비지 컬렉션(GC, Java가 안 쓰는 객체를 자동으로 메모리에서 정리하는 기능) 대상이 됩니다. 1000회 반복이면 약 1000개의 String 객체가 누적되어 성능과 메모리에 부담을 줍니다.

StringBuilder — 가변 문자열 심화

StringBuilder 는 내용을 수정할 수 있는(가변) 문자열 버퍼입니다. append()로 문자를 덧붙여도 객체가 새로 만들어지지 않고 같은 객체의 내부 배열2에 덧붙습니다.

java
코드 하이라이팅 중...
StringBuilder — 같은 객체의 내부 배열에 누적
반복해도 sb가 가리키는 주소는 100으로 고정됩니다. 새 객체를 만들지 않고 같은 객체의 내부 배열만 자라납니다. 1000회 반복해도 객체는 1개.

String vs StringBuilder 비교

구분StringStringBuilder
가변성불변 (immutable)가변 (mutable)
반복 연결 객체 수반복마다 새 객체같은 객체 유지
주요 메서드+, concat()append(), insert(), delete(), reverse()
속도 (반복 연결)느림빠름
용도문자열이 거의 바뀌지 않을 때반복 수정이 필요할 때

참조 동일성 차이

StringBuilder는 객체가 바뀌지 않으므로 append() 전후의 참조가 동일합니다. 반면 String은 매 연결마다 새 객체이므로 참조가 바뀝니다.

java
코드 하이라이팅 중...

==는 두 변수가 같은 주소를 가리키는지 비교합니다. 내용이 같아도 다른 주소면 false가 됩니다. 내용 비교는 equals()로 따로 해야 합니다.

StringBuilder는 같은 객체 공유, String은 새 객체로 분리
StringBuilder의 sb1, sb2는 같은 주소 100을 공유하므로 sb1 == sb2 → true. 반면 String의 s1은 새 객체(주소 300)로 갈아끼워졌고 s2는 옛 객체(주소 200)에 남아 s1 == s2 → false.

StringBuilder 주요 메서드

java
코드 하이라이팅 중...

시작값 "Hello"를 기준으로 각 메서드를 단독 호출했을 때의 결과입니다.

메서드동작예시결과
append(x)끝에 덧붙임sb.append("W")"HelloW"
insert(i, x)i번 자리에 삽입sb.insert(0, "A")"AHello"
delete(a, b)a ~ b-1 범위 삭제sb.delete(1, 3)"Hlo"
reverse()문자열 뒤집기sb.reverse()"olleH"
toString()String으로 변환sb.toString()"Hello"
length()현재 길이sb.length()5

insertdelete는 인덱스를 어떻게 다루는지 그림으로 보면 이해가 쉽습니다.

StringBuilder의 insert / delete 인덱스 동작
insert(i, x)는 인덱스 i 자리에 글자를 끼워 넣고 뒤 글자를 한 칸씩 밀어냅니다. delete(a, b)는 인덱스 a부터 b-1까지 삭제합니다(끝 인덱스 b는 포함하지 않는 반열린 구간).

연습 문제


Footnotes

  1. Garbage Collector(GC): Java가 더 이상 참조되지 않는 객체를 자동으로 메모리에서 해제해주는 기능. C언어의 free()처럼 직접 해제 함수를 호출할 필요가 없음.

  2. StringBuilder는 내부에 글자를 담는 배열을 들고 있고, append 시 그 배열의 빈 칸에 글자를 채워 넣습니다. 자리가 부족해지면 더 큰 배열로 한 번 옮겨 담습니다.


String 불변성과 StringBuilder | 정처기 감자