본문 바로가기

Java

String, StringBuffer, StringBuilder 클래스

- String 클래스는 자바에서 가장 많이 사용되는 클래스 중 하나입니다. 

  따라서 String 클래스에 대해 잘 알아두는 것은 자바 개발자의 기본입니다. 

  이번 글에서는 String 클래스의 특징과,

  String 클래스의 단점을 보완하는 StringBuffer, StringBuilder 클래스를 알아보겠습니다.

 

1) String 클래스란?

- String 클래스는 문자열 객체를 생성하기 위한 클래스입니다. 

  문자열이란 문자들의 집합을 의미합니다. 

  자바 공식 문서를 보면 String 클래스는 다음과 같이 정의되어 있습니다. 

public final class String
extends Object
implements Serializable, Comparable<String>, CharSequence

- 우선 첫번째 줄에 public final class String 이라고 String 클래스가 선언되어 있습니다. 

   즉, String 클래스는 불변(final) 클래스입니다. 

   불변 클래스의 특징은 상속이 불가능하다는 점입니다.  

   상속이 불가능하다는 의미는 자식 클래스를 가질 수 없다는 의미이며, 

   자식 클래스를 가질 수 없다는 것은 메소드의 오버라이딩이 불가능하다는 의미입니다. 

   따라서 String 클래스는 자식 클래스를 가질 수 없으며, 변경이 불가능합니다. 

  

- 두번째 줄에는 extends Object가 선언되어 있습니다. 

   자바의 모든 클래스는 Object 클래스를 상속해야 하기 때문입니다.

   그리고 세번째 줄에는 implements Serializable, Comparable<String>, CharSequence가 선언되어 있습니다.

   이것의 의미는 String 클래스가 Serializable, Comparable<String>, CharSequence 이렇게 3개의 인터페이스를

   구현했다는 의미입니다. 

 

- Serializable 인터페이스는 객체를 파일에 저장하거나 다른 서버로 전송 가능한 상태로 만들어줍니다. 

  여기서 Serializable이란 '직렬화 가능한'의 의미입니다.

  즉, String 클래스는 Serializable 인터페이스를 구현함으로써 직렬화가 가능합니다. 

 

※ 직렬화란?

- 직렬화란 자바 객체를 Byte stream으로 변환하는 작업을 의미합니다. 

   반대로 역직렬화란 Byte stream을 자바 객체로 변환하는 작업을 의미합니다. 

 

- 다음은 Comparable 인터페이스를 구현했음을 알 수 있습니다.   

   Comparable 인터페이스는 딱 하나의 메소드, compareTo 메소드만을 갖고 있습니다. 

   compareTo 메소드는 객체의 순서를 비교하기 위한 메소드입니다.

   compareTo 메소드는 다음과 같이 선언됩니다.

int compareTo(T o)

   여기서 T는 제네릭을 의미하며, o는 객체를 나타냅니다.

   compareTo 메소드는 현재 객체가 비교 객체(o)보다 순서 상으로 앞에 있으면 -1을,

   같으면 0을, 순서 상으로 뒤에 있으면 1을 반환합니다. 

 

- 마지막으로 CharSequence 인터페이스를 구현했음을 알 수 있습니다. 

  CharSequence 인터페이스는 해당 클래스가 문자열을 다루기 위한 클래스임을 나타내기 위해서 사용됩니다.

  CharSequence는 다른 문자열 클래스인 StringBuffer, StringBuilder 클래스에도 구현되어 있습니다. 

 

 

2) String 객체 생성

-  자바의 모든 객체는 반드시 new 키워드를 통해서 생성해야 합니다. 

   하지만 String 객체는 자바의 다른 객체와는 다르게 2가지 방법으로 생성할 수 있습니다. 

  그것은 바로 

  (1) ""를 통해서 생성하는 것

  (2) new 키워드를 통해서 생성하는 것입니다. 

 

 

  (1) ""를 통해서 생성

-  String 객체는 ""를 통해서 생성한다는 것은 다음과 같이 생성하는 것을 의미합니다. 

String str1 = "Hello";

- String str1 = "Hello"로 String 객체를 생성하면 str1 변수는 스택 영역에, "Hello"는 힙의 String constant pool에 저장됩니다. 이렇게 String constant pool이라는 것을 활용하는 이유는 무엇일까요?

그 이유는 다음 코드와 그림을 보면 이해할 수 있습니다. 

String str1 = "Hello";
String str2 = "Hello";

 

- 다음과 같이 String str2 = "Hello"라는 String 객체가 선언되면, "Hello"라는 문자열을 새로 생성하는 것이 아니라

  기존에 힙의 String constant pool에 있는 것을 재활용합니다. 

  따라서 ""로 String 객체를 선언하면, 같은 문자열 객체에 대해서 해당 문자열을 재활용하므로,  

  메모리를 절약할 수 있다는 이점이 있습니다. 

 

(2) new를 통해서 생성

- 반면 new를 통해서 String 객체를 생성할 수도 있습니다. 

String str1 = new String("John");
String str2 = new String("Doe");

- 이러한 경우는 문자열의 값과 상관없이 String constant pool이 아닌 힙 영역에 매번 객체가 새로 생성됩니다.

  따라서 new를 통해서 String 객체를 생성하는 경우는 객체를 재활용할 수 없습니다. 

   

 

3) StringBuffer, StringBuilder 클래스

- 위에서 String 클래스는 final 키워드가 붙은 불변 클래스임을 알아봤습니다.

  이는 String 클래스를 안전하게 만들어주지만, 동시에 새로운 문제를 발생시킵니다. 

  그것은 String 문자열을 더하는 작업을 할 때마다, 새로운 String 객체가 생성되고 기존 객체는 버려진다는 점입니다.

  그 이유는 String 객체가 불변 객체이므로 값의 변경이 불가능하기 때문입니다.   

  이는 계속해서 사용하지 않는 객체를 만들어내므로 메모리의 낭비로 이어질 수 있습니다. 

 

- 따라서 자바에서는 String 클래스의 단점을 보완하기 위해 StringBuffer, StringBuilder 클래스를 제공합니다. 

  StringBuffer, StringBuilder 클래스는 append라는 메소드를 제공합니다.

  이를 통해 새로운 문자열 객체를 생성하지 않고, 기존 문자열 객체에 새로운 문자열 객체를 더할 수 있습니다.

StringBuilder sb = new StringBuilder();
sb.append("Hello");
sb.append(" World");

- StringBuffer 클래스와 StringBuilder 클래스의 차이점은 StringBuffer 클래스는 스레드 안전(thread-safe)하고, StringBuilder 클래스는 스레드 안전하지 않다는 점입니다.

  이는 StringBuffer 클래스는 메소드에 synchronized 키워드가 붙어 있지만, StringBuilder 클래스는 그렇지 않다는 

  차이점에서 발생합니다. 

  [StringBuffer]

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

  [StringBuilder]

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

- StringBuffer 클래스는 synchronized로 동기화 처리가 되어 있기 때문에, 한 번에 하나의 스레드만 접근할 수 있습니다.

  따라서 동기화 문제가 발생하지 않지만, 속도가 느리다는 단점이 있습니다. 

  반면, StringBuilder 클래스는 동기화 처리를 하지 않기 때문에, 동기화 문제가 발생할 수 있고, 

  속도는 StringBuffer 클래스에 비해 빠릅니다. 

 

- 따라서 여러 스레드에서 문자열 객체에 동시에 접근하는 일이 있는 경우는, StringBuffer 클래스를,

  그렇지 않은 경우는 속도가 더 빠른 StringBuilder 클래스를 사용하는 것이 좋습니다. 

 

 

참고

자바의 신 1권

Serialization and Deserialization in Java with Example - GeeksforGeeks  

String (Java Platform SE 7 ) (oracle.com)

StringBuffer (Java Platform SE 7 ) (oracle.com)  

StringBuilder (Java Platform SE 7 ) (oracle.com)   

String Constant Pool in Java - GeeksforGeeks

'Java' 카테고리의 다른 글

생성자 대신 정적 팩토리 메서드  (0) 2022.08.25
정규 표현식  (0) 2022.08.25
리플렉션  (0) 2022.08.17
자바 가비지 컬렉션(1)  (0) 2022.08.11
자바에서 소스 코드가 실행되는 과정  (0) 2022.08.10