String, StringBuffer, StringBuilder? – 문자열 처리의 모든 것

String, StringBuffer, StringBuilder의 차이 – 문자열 처리의 모든 것
문자열은 자바에서 가장 많이 사용되는 데이터 유형 중 하나다. 자바에서는 문자열을 처리하기 위해 String, StringBuffer, StringBuilder라는 세 가지 주요 클래스를 제공한다.
String: 불변 객체(Immutable)와 메모리 구조
- String은 불변(Immutable): 한 번 생성된 String 객체는 변경할 수 없다. 문자열을 수정하려고 하면 새로운 객체를 생성하여 변경된 값을 저장한다.
- 메모리 구조: String 리터럴은 JVM의 String Constant Pool에 저장되며, 동일한 값의 문자열은 같은 참조를 공유한다.
장점 | 단점 |
|
|
StringBuffer: 동기화(Synchronized) 지원
- 가변 객체(Mutable): StringBuffer 객체는 생성 후에도 내용을 변경할 수 있다.
- 동기화(Synchronized): 멀티스레드 환경에서 쓰레드 안전성을 보장한다.
장점 | 단점 |
|
|
StringBuilder: 비동기화(Unsynchronized)로 빠른 성능
- 가변 객체(Mutable): StringBuilder는 StringBuffer와 동일하게 문자열을 수정할 수 있다.
- 비동기화(Unsynchronized): 동기화를 지원하지 않으므로 단일 쓰레드 환경에서 빠른 성능을 제공한다.
장점 | 단점 |
|
|
요약
특징 |
String | StringBuffer | StringBuilder |
변경 가능성 | 불변(Immutable) | 가변(Mutable) | 가변(Mutable) |
동기화 지원 여부 | 지원하지 않음 | 지원 (Synchronized) | 지원하지 않음 (Unsynchronized) |
사용 환경 | 불변성을 유지해야 할 때 | 멀티스레드 환경 | 단일 쓰레드 환경 |
성능 | 문자열 수정 시 느림 | 동기화로 인해 느릴 수 있음 | 가장 빠름 |
성능 테스트
package test;
public class PerformanceTest {
public static void main(String[] args) {
long startTime, endTime;
// String 성능 테스트
startTime = System.nanoTime();
String str = "";
for (int i = 0; i < 10000; i++) {
str += i; // 새로운 객체가 계속 생성됨
}
endTime = System.nanoTime();
System.out.println("String 시간: " + (endTime - startTime) + "ns");
// StringBuffer 성능 테스트
startTime = System.nanoTime();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < 10000; i++) {
sb.append(i); // 기존 객체 수정
}
endTime = System.nanoTime();
System.out.println("StringBuffer 시간: " + (endTime - startTime) + "ns");
// StringBuilder 성능 테스트
startTime = System.nanoTime();
StringBuilder sbuilder = new StringBuilder();
for (int i = 0; i < 10000; i++) {
sbuilder.append(i); // 기존 객체 수정
}
endTime = System.nanoTime();
System.out.println("StringBuilder 시간: " + (endTime - startTime) + "ns");
}
}

- String은 새로운 객체를 계속 생성하므로 가장 느리다.
- StringBuffer는 동기화가 필요하므로 약간 느리다.
- StringBuilder는 동기화를 사용하지 않으므로 가장 빠르다.
면접 예상 질문
Q. String은 왜 불변(Immutable)인가?
- 보안성: 불변 객체는 외부에서 값을 변경할 수 없으므로, 보안이 요구되는 환경(예: 네트워크 연결, 데이터베이스 URL)에서 안전하다.
String url = "jdbc:mysql://localhost:3306/mydb";
// 외부 코드에서 url을 변경하지 못함
- 쓰레드 안전성: 불변 객체는 멀티스레드 환경에서도 동시 접근 시 안전하다.
- 캐싱 효과: 동일한 값의 String은 JVM의 String Constant Pool에서 공유되므로, 메모리를 절약하고 성능을 높일 수 있다.
String str1 = "Hello";
String str2 = "Hello";
System.out.println(str1 == str2); // true (String Constant Pool에서 동일 객체 참조)
Q. Java에서 문자열을 조작하기 위한 클래스는 무엇입니까? 그들 사이의 차이점은 무엇입니까?
String은 불변 객체이고 문자열 수정 시 새로운 객체를 생성하여 성능 저하가 발생할 수 있습니다.
StringBuffer는 가변 객체로 동기화를 지원하여 멀티스레드 환경에서 안정적입니다. String과 다르게 문자열 수정시 기존 객체를 변경합니다.
StringBuilder는 가변 객체로 동기화를 지원하지 않으므로 단일 스레드 환경에서 가장 빠르게 동작합니다.
Q. immutable에 대해서 설명하시오.
객체의 상태가 한 번 생성된 후 변경되지 않는 특성을 의미한다. 자바의 String, Integer, BigDecimal 등이 불변 객체입니다. 불변 객체는 값 변경 시 새로운 객체를 생성하고 값이 고정되어 변경될 수 없기 때문에 멀티 스레드 환경에서 안전합니다. 또한 내부의 데이터가 외부에서 변경되지 않아서 안정성과 보안성을 제공합니다.
Q. 아래의 결과를 예측하고, 이유를 설명하시오.
String str1 = "Simple String";
String str2 = "Simple String";
String str3 = new String("Simple String");
String str4 = new String("Simple String");
//참조변수의 참조 값 비교
if(str1 == str2)
System.out.println("str1과 str2는 동일 인스턴스 참조");
else
System.out.println("str1과 str2는 다른 인스턴스 참조");
if(str3 == str4)
System.out.println("str3과 str4는 동일 인스턴스 참조");
else
System.out.println("str3과 str4는 다른 인스턴스 참조");
str1과 str2는 동일 인스턴스 참조
str3과 str4는 다른 인스턴스 참조
위와 같이 출력됩니다.
str1과 str2 를 보면, str1은 String Constant Pool에 저장됩니다. str2는 같은 문자열 값을 참조하므로 동일한 객체를 참조하게 됩니다
- 따라서 str1 == str2는 true.
str3과 str4 를 보면 new String("~~");은 항상 Heap 메모리에 새로운 객체를 생성합니다. 그래서 str3과 str4는 서로 다른 객체를 참조합니다.
- 따라서 str3 == str4는 false.
Q. 이전 문제의 메모리를 설명하시오.
자바에서 문자열은 Heap 메모리와 String Constant Pool 두 가지 영역에 저장됩니다.
- String Constant Pool는 문자열 리터럴 값이 저장되는 공간으로, 동일한 문자열이 여러 번 선언되면, 같은 객체를 참조하여 메모리를 절약합니다.
- Heap 메모리는 new 키워드를 통해 생성된 객체가 저장되는 공간으로, 동일한 값을 가지더라도, 각각 독립된 객체로 생성됩니다.
Q. 아래가 실행되는 원리를 표현해 보세요.
String str = "age" + 17;
자바에서 + 연산자는 내부적으로 StringBuilder를 사용해 문자열을 연결합니다. 아래 코드와 같이 컴파일러가 다음과 같이 변환합니다.
String str = new StringBuilder("age")
.append(17)
.toString();
- 문자열 연산(+)은 불변 객체인 String을 사용하면 매번 새로운 객체를 생성해야 하지만, 이를 최적화하기 위해 컴파일러가 StringBuilder를 사용해 성능을 개선합니다.