Java의 문자열 클래스
Java의 대표적인 문자열 클래스는 String, StringBuffer, StringBuilder 가 있습니다.
모두 문자열을 저장하고, 관리하는 클래스이지만 조금의 차이가 있습니다.
String 이란?
"String 클래스의 가장 큰 특징은 불변(immutable)하다는 것입니다."
즉 문자열이 할당된 메모리 공간이 절대 변하지 않습니다.
// String str = new String("hello");
String str = "hello";
str = str + "java";
- 처음 str 변수에 "hello" 문자열을 넣었습니다.
- 이후 str 변수에 "java" 문자열을 더했습니다.
- 이 과정을 "hello" 값을 가지고 있던 str 변수가 가리키는 곳에 저장된 "hello"에 "java" 문자열을 더해 변경한 것으로 착각할 수 있습니다.
- 하지만 String 클래스의 가장 큰 특징은 불변(immutable)하다는 것입니다. 내부적으로는 아래와 같이 동작합니다.
- String 클래스의 참조 변수 str이 "hello java"라는 값을 가지고 있는 새로운 메모리 영역을 가리키게 변경됩니다.
- 처음 선언했던 "hello" 값이 할당되어 있던 메모리 영역은 Garbage로 남아있다가 GC(garbage collection)에 의해 사라지게 됩니다.
- String 클래스는 불변(immutable)하기 때문에 수정하는 시점에서 새로운 String 인스턴스가 생성됩니다.
정리하자면
문자열에 + 연산자 등을 사용하여 수정이 발생할 때 기존 문자열에 새로운 문자열이 추가되는 것이 아니라 새로운 문자열 객체를 만들고 그 객체를 참조하게 합니다.
따라서 레퍼런스가 가리키고 있던 문자열이 다른 문자열로 대체되면 기존 문자열은 레퍼런스 참조가 사라진 상태(Unreachable)가 되어 GC(garbage collection)의 대상이 됩니다.
장점으로는 아래서 설명한 String Pool에 의해 변하지 않는 문자열을 자주 읽어들이는 경우 좋은 성능을 기대할 수 있습니다.
단점으로는 문자열 추가, 수정, 삭제 등의 연산이 빈번하게 발생하는 경우 힙(heap) 메모리에 많은 임시 가비지(Garbage)가 생성되어 힙(heap) 메모리 부족으로 성능에 치명적인 영향을 끼칠 수 있습니다.
String Pool 이란?
// 리터럴 생성 방식 일반적으로 사용하는 방식
String literalStr = "literal";
// new 키워드를 통해 인스턴스 생성 방식
String newStr = new String("literal");
위와 같이 두가지 생성 방법이 있습니다. 두 방식 모두 JVM 메모리 중 힙(heap) 영역에 생성이 됩니다.
하지만 리터럴 방식으로 생성을 하게 되면 "String Pool"이라는 공간에 생성이 됩니다.
String str1 = "literal";
String str2 = "literal";
String str3 = "literal";
처음 "literal" 문자열을 넣은 String 변수를 선언했을 때 "literal"이라는 문자열을 String Pool에서 생성했기 때문에
이후 생성한 변수들을 추가적으로 생성하지 않고 똑같은 문자열(같은 레퍼런스)을 가리킵니다.
StringBuffer와 StringBuilder 란?
"String 클래스의 불변(immutable)과 반대로 변경 가능(mutable)하다."
즉 문자열 연산을 할 때 인스턴스(new)를 한 번만 생성하고 메모리의 값을 변경시켜 문자열을 변경합니다.
문자열 연산 등으로 기존 객체의 공간이 부족하게 되는 경우 기존의 버퍼 크기를 늘리며 유연하게 동작합니다.
StringBuffer와 StringBuilder 생성, 사용 예시입니다.
// StringBuffer 생성
StringBuffer sb = new StringBuffer("stringBuffer");
sb.substring(2, 4); 문자열 추출
sb.insert(2, "추가"); // 문자열 추가
sb.delete(2, 4); // 문자열 삭제
sb.append("append"); // 문자열 이어 붙이기
sb.length(); // 문자열 길이 구하기
sb.capacity(); // 용량의 크기 구하기
sb.reverse(); // 문자열 뒤집기
// StringBuilder 생성
StrinBuilder sb1 = new StringBuilder("stringBuilder");
sb1.substring(2, 4); 문자열 추출
sb1.insert(2, "추가"); // 문자열 추가
sb1.delete(2, 4); // 문자열 삭제
sb1.append("append"); // 문자열 이어 붙이기
sb1.length(); // 문자열 길이 구하기
sb1.capacity(); // 용량의 크기 구하기
sb1.reverse(); // 문자열 뒤집기
StringBuffer와 StringBuilder의 차이점
"동기화의 여부의 차이로 멀티 쓰레드 환경에서의 안정성"
StringBuffer는 각 메소드 별로 Synchonized Keyword가 존재하여, 멀티 쓰레드 환경에서도 동기화가 가능합니다.
즉 멀티 쓰레드 환경에서의 안전성(thread-safe)을 가지고 있습니다.
StringBuilder는 동기화를 지원하지 않기 때문에 멀티 쓰레드 환경에서는 적합하지 않지만,
동기화를 고려하지 않는 싱글 쓰레드 환경에서는 StringBuffer에 비해 연산 처리가 빠른 장점이 있습니다.
정리
클래스 | 사용 개발 환경 |
String | 조회가 많은 경우, 멀티 쓰레드 환경 |
StringBuffer | 문자열 연산이 자주 발생하는 경우, 멀티 쓰레드 환경 (동기화를 지원하기 때문에 Thread-safe 하다) |
StringBuilder | 문자열 연산이 자주 발생하는 경우, 싱글 쓰레드이거나 동기화를 고려하지 않아도 되는 환경 |
참고 자료