코딩마을방범대
[Java] String, StringBuffer, StringBuilder의 차이점 본문
여태 문자열을 이어붙일 때 StringBuilder 같은 건 선언하기 귀찮기도 하고 그렇게 친숙하진 않아서 String + 로 추가해왔는데
StringBuilder와 StringBuffer에 대해 알아봐야할 필요성을 느꼈다.
String
- 불변(immutable) 자료형
( 인스턴스 생성 시 생성자의 인자 value는 상수(final)로 선언되어있다. )
String 객체를 불변하게 설계한 이유는 캐싱, 보안, 동기화, 성능측면 이점을 얻기 위해서이다.
1. 캐싱 : String을 불변하게 함으로써 String pool에 각 리터럴 문자열의 하나만 저장하며 다시 사용하거나 캐싱에 이용가능하며 이로 인해 힙 공간을 절약할 수 있다는 장점이 있다.
2. 보안 : 예를 들어 데이터베이스 사용자 이름, 암호는 데이터베이스 연결을 수신하기 위해 문자열로 전달되는데, 만일 번지수의 문자열 값이 변경이 가능하다면 해커가 참조 값을 변경하여 애플리케이션에 보안 문제를 일으킬 수 있다.
3. 동기화 : 불변함으로써 동시에 실행되는 여러 스레드에서 안정적이게 공유가 가능하다.
String 객체는 한 번 생성하면 상수값을 가진 객체로 생성되어, concat 또는 + 또는 덮어쓰기 같은 수정을 진행해도
실질적인 값은 변경되지 않고 새로운 String을 생성한 후 변수가 참조하는 Heap 영역의 객체 주소값이 변경되는 것이다.
새로운 String을 생성하는 것이 무슨 말이냐 하면,
만약 "hello"와 "world"라는 문자열을 합치려고 할 때 + 를 사용했다면 StringBuilder를 생성해 append를 시켜준 후 toString() 메소드를 이용해 String 값으로 변환하여 돌려준다.
// 아래 두 개의 로직이 동일하다.
String str1 = "hello" + "world";
String str2 = new StringBuilder("hello").append("world").toString();
concat을 사용했다면 생성자를 이용해 새로운 String을 생성한 후 리턴해준다.
아래는 String 클래스의 concat 메소드이다.
public String concat(String str) {
int otherLen = str.length();
if (otherLen == 0) {
return this;
}
int len = value.length;
char buf[] = Arrays.copyOf(value, len + otherLen);
str.getChars(buf, len);
return new String(buf, true);
}
따라서 변경된 후의 객체 주소값이 해당 변수에 참조가 되고,
기존의 String 객체는 Heap 영역에 떠돌다가 GC(가비지 컬렉션)에 의해 지워진다.
가비지 컬렉션은 Unreachable 상태인 객체를 제거한다.
Reachable : 객체가 참조되고 있는 상태
Unreachable : 객체가 참조되고 있지 않은 상태
리터럴과 생성자를 이용한 String 생성 시의 차이점
리터럴 | string constant pool 이라는 영역에 존재 |
생성자 | Heap 영역에 존재 |
리터럴로 선언 시
내부적으로 String의 intern() 메소드가 호출되고,
intern() 메소드는 주어진 문자열이 pool 에 존재하는지 검색하고 있다면 그 주소값을 반환하고
없으면 pool에 생성 후 새로운 주소값을 반환한다.
StringBuffer & StringBuilder
- 가변(mutable)적
( 내부 Buffer(데이터를 임시로 저장하는 메모리)에 문자열을 저장한 후 수정,삭제 작업을 한다. ) - 기본 16 버퍼 크기로 생성된 후 문자열 연산 등으로 기존 객체의 공간이 부족하게 되는 경우,
기존의 버퍼 크기를 늘리며 유연하게 동작
StringBuffer와 StringBuilder의 차이점
클래스 | 설명 |
StringBuffer | 각 메소드별로 Synchronized Keyword가 존재하여, 멀티스레드 환경에서도 동기화를 지원 |
StringBuilder | 동기화를 보장하지 않음 ( 쓰레드가 객체에 접근해서 변경을 하면 기다려주지 않음 ) |
Synchronized Keyword
여러개의 스레드가 한 개의 자원에 접근할려고 할 때,
현재 데이터를 사용하고 있는 스레드를 제외하고 나머지 스레드들이 데이터에 접근할 수 없도록 막는 역할을 수행
결론
클래스 | 설명 |
String | 문자열 추가 연산이 적고, 쓰레드 세이프 환경일 경우 ( 간단하게 사용 ) |
StringBuffer | 문자열 추가 연산이 많고, 쓰레드 세이프 환경일 경우 |
StringBuilder | 문자열 추가 연산이 많고, 단일 쓰레드 환경이고, 빠른 연산이 필요한 경우 |
💡 연산이 많은 경우 💡
StringBuilder >> StringBuffer >> String
문자열 자료형의 값 비교
String은 equals()를 통해 문자열 데이터 동등 비교가 가능하다.
하지만 StringBuilder와 StringBuffer는 equals 메소드를 오버라이딩하지 않기 때문에 문자열 데이터를 비교할 수 없어 ==과 같은 결과를 가져온다.
따라서 StringBuilder와 StringBuffer는 toString()을 이용해 String으로 변환하여 비교해야 한다.
== 는 문자열 데이터를 비교하는 것이 아닌,
동일한 객체를 참조( = 힙 영역의 객체 주소를 비교 )하는지에 대한 결과가 출력된다.
StringBuffer sb = new StringBuffer("hello");
StringBuffer sb2 = new StringBuffer("hello");
System.out.println(sb == sb2); // false
System.out.println(sb2.equals(sb)); // false
System.out.println(sb2.toString().equals(sb.toString())); // true
참고사이트
[자바] String, StringBuilder, StringBuffer의 차이
☕ 자바 String / StringBuffer / StringBuilder 차이점 & 성능 비교
'💡 백엔드 > Java' 카테고리의 다른 글
[Java] 컬렉션 팩토리(Set, Map, List) (0) | 2023.07.31 |
---|---|
List 를 String 으로, String 을 List 로 변환하는 방법 (0) | 2023.07.28 |
자바에서 유니크한 값 가져오기 ( UUID / random ) (0) | 2023.07.27 |
Java에서 QR 코드 만들기 (0) | 2023.07.27 |
Java의 Reflection 기능을 사용하는 방법 (0) | 2023.07.26 |