자바 & 스프링

java - String, StringBuilder, StringBuffer

p829911 2021. 12. 8. 18:30

String, StringBuilder, StringBuffer는 자바에서 문자열을 다루는 대표적인 클래스이다.

 

String

Java에서 String 객체를 생성하는 방법은 2가지가 있는데 하나는 "" 큰 따옴표를 사용하는 것이고, 두번째는 new 연산자를 사용하는 것이다.
String은 new를 쓰지 않고 객체를 생성할 수 있는 특이한 클래스이다.
이 두 가지 방법의 객체 할당 방식에는 차이가 있다.

String str1 = "abcde";
String str2 = "abcde";

String str3 = new String("abcde");
String str4 = new String("abcde");

System.out.println(str1 == str2); // true
System.out.println(str3 == str4); // false
System.out.println(System.identityHashCode(str1)); // 745160567
System.out.println(System.identityHashCode(str2)); // 745160567
System.out.println(System.identityHashCode(str3)); // 610984013
System.out.println(System.identityHashCode(str4)); // 1644443712

큰 따옴표로 생성한 객체는 내용이 같다면 같은 객체를 가리키지만, new 연산자로 생성한 객체는 내용이 같더라도 개별적인 객체를 생성한다. 이는 java 의 String Pool 때문이다.

"Cat"을 literal로 생성하면 string pool에 저장된다. 그 다음부터 literal로 "Cat" 이라는 똑같은 단어가 생성되면 string pool에 있는 "Cat"을 가리키게 된다. 하지만 new String("Cat") 으로 생성할 경우 Heap영역 내 별도 객체를 생성하여 가리키게 된다.

String의 특성인 불변객체의 장점을 누리기 위해서는 "" 로 String을 생성해야 한다.

String Constant Pool을 직접 보고싶다면 아래와 같은 경우 javac StringPool.java -> javap -verbose -private StringPool 로 결과를 볼 수 있다. 실제 String literal 간의 연산만 String Pool 에 저장되고 객체와 더하기 연산이 들어가면 또 heap 에 저장되는 것으로 보인다.

public class StringPool {
    public static void main(String[] args) {
        String a = "Hello";
        String b = "World";
        String c = a + "World";
        String d = b + "World";
        String e = a + b;
        String f = "Hello" + "World";
        String g = "HelloWorld";

        System.out.println("a = " + System.identityHashCode(a)); // 1684106402
        System.out.println("b = " + System.identityHashCode(b)); // 788117692
        System.out.println("c = " + System.identityHashCode(c)); // 1566723494
        System.out.println("d = " + System.identityHashCode(d)); // 510113906
        System.out.println("e = " + System.identityHashCode(e)); // 1622006612
        System.out.println("f = " + System.identityHashCode(f)); // 66233253
        System.out.println("g = " + System.identityHashCode(g)); // 66233253
    }
}

StringBuilder, StringBuffer

String은 불변이다.
따라서 String 에 더하기 연산을 해서 문자열을 이어 붙이면 기존 문자열에 추가 문자열이 연결되는게 아니고,
더해진 문자열이 새로 생성된다.

이런 로직 때문에 문자열을 읽는 연산은 효율적이지만, 추가, 수정, 삭제 등의 연산이 빈번하게 발생한다면, 힙 메모리에 많은 garbage가 생성되기 때문에 어플리케이션 성능에 영향을 끼치게 된다.

이를 해결하기 위해 가변성을 가지는 StringBuilder StringBuffer 클래스가 있다. 이 클래스를 사용하면

.append(), .delete() 등의 API를 이용해서 동일 객체 내에서 문자열을 변경하는 것이 가능하다.

StringBuilder builder = new StringBuilder("Hello");
builder.append(" World");
System.out.println(builder); // Hello World

StringBuffer buffer = new StringBuffer("Hello");
buffer.append(" World");
System.out.println(buffer); // Hello World

StringBuilder와 StringBuffer는 생성 시 할당하는 저장용량 capacity (16) 을 가지고 있으며, append 연산으로 저장용량을 초과하는 경우 기존 저장용량의 1.5배가 되는 새로운 저장용량을 가진 객체를 재할당하게 된다. 그렇기 때문에 예상하는 최종 문자열 길이가 있다면 아래와 같은 생성자를 이용해 초기에 객체를 생성할 때 저장용량을 미리 지정해줘서 사용해야 효율적이다.

public StringBuffer(int capacity) {
    super(capacity);
}

StringBuffer와 StringBuilder의 차이는 thread-safe 이다.

@Override
public synchronized int compareTo(StringBuffer another) {
    return super.compareTo(another);
}

@Override
public synchronized int length() {
    return count;
}

@Override
public synchronized int capacity() {
    return super.capacity();
}


@Override
public synchronized void ensureCapacity(int minimumCapacity) {
    super.ensureCapacity(minimumCapacity);
}

/**
* @since      1.5
*/
@Override
public synchronized void trimToSize() {
    super.trimToSize();
}

StringBuffer의 메소드들에는 synchronized 키워드가 붙어있지만 StringBuilder의 메소드들에는 synchronized 키워드가 없다.

 

참고

 

 

String Constant Pool이란? | Java String Pool

Java에서 String 객체를 생성하는 방법은 2가지가 있다. 첫번째는 String literal, 즉 큰 따옴표("")를 사용하는 것이고, 두번째는 new 연산자를 사용하는 것이다. 두 방법에는 어떤 차이가 있을까? 간단한

starkying.tistory.com

 

[Java] String, StringBuffer, StringBuilder 차이 및 장단점

Java 에서 문자열을 다루를 대표적인 클래스로 String , StringBuffer, StringBuilder 가 있습니다. 연산이 많지 않을때는 위에 나열된 어떤 클래스를 사용하더라도 이슈가 발생할 가능성은 거의 없습니다

ifuwanna.tistory.com

 

'자바 & 스프링' 카테고리의 다른 글

ThreadLocal의 정의와 사용법  (0) 2021.12.20
트랜잭션  (0) 2021.12.20
JDBC Connection pool은 왜 필요할까?  (0) 2021.12.01
java의 싱글톤  (0) 2021.12.01
PreparedStatement 쿼리를 사용하는 이유  (0) 2021.11.30