반응형

자바에서 문자열을 처리하는 방법에는 String, StringBuilder, StringBuffer가 있습니다. 

 

시간이 없다면, 맨 아래로..

한 줄로 정리하자면..

이들은 각각 고정된 문자열, 가변적인 문자열, 스레드 안전한 가변적인 문자열을 처리하는 데 사용됩니다.


1. String

먼저, String 클래스는 불변 객체입니다.

이것은 생성된 문자열이 수정되지 않는다는 것을 의미합니다.

String 클래스는 final 키워드가 지정되어 있기 때문에 하위 클래스에서 재정의 할 수 없습니다.

따라서, String 클래스의 메소드들은 문자열을 수정하는 것이 아니라 새로운 문자열을 생성합니다.

예를 들어, String 클래스의 concat() 메소드는 기존 문자열과 다른 문자열을 연결하여 새로운 문자열을 반환합니다.

 

2. String과 StringBuilder, String Buffer의 차이

반면에 StringBuilder와 StringBuffer 클래스는 가변적인 문자열을 처리합니다.

StringBuilder와 StringBuffer는 둘 다 append()와 insert() 메소드를 제공하여 문자열을 수정할 수 있습니다.

또한, StringBuilder와 StringBuffer는 내부 버퍼를 가지고 있으며, 필요에 따라 버퍼 크기를 조정할 수 있습니다.

 

 

StringBuilder스레드 안전하지 않지만 빠른 속도를 제공합니다.

반면에 StringBuffer스레드 안전하지만 느린 속도를 제공합니다.

 

 

3. 비교 방법이 다른 이유 (내용 vs 주소)

또한, String 클래스와 StringBuilder/StringBuffer 클래스의 문자열 비교 방법도 다릅니다. 

String 클래스에서는 equals() 메소드를 사용하여 문자열을 비교합니다. 

equals() 메소드는 문자열 내용이 같은지를 비교합니다. 

반면에 == 연산자는 두 개의 객체가 같은 객체인지를 비교합니다.

 

즉, == 연산자는 두 개의 문자열이 같은지를 비교하는 것이 아니라, 두 개의 참조(=주소)가 같은지를 비교하는 것입니다.
아래는 String 클래스에서의 == 연산자 예시입니다.

String s1 = "Hello World";
String s2 = "Hello World";
System.out.println(s1 == s2); //true

위 코드에서 s1과 s2는 같은 문자열을 참조하고 있으므로 == 연산자는 true를 반환합니다.

4. StringBuilder 와 StringBuffer 의 차이

StringBuilder 클래스는 StringBuffer와 유사하게 문자열을 가변적으로 조작할 수 있도록 해줍니다. 

 

하지만 StringBuffer는 멀티스레드 환경에서 안전하도록 구현되어 있어서, 동기화 관련 오버헤드가 있습니다. 

그에 반해 StringBuilder는 단일 스레드 환경에서만 사용하도록 고안되어 있어서, 동기화 처리를 하지 않기 때문에 더 빠르게 문자열을 조작할 수 있습니다.

StringBuilder의 메서드는 StringBuffer와 유사하게 구성되어 있어서, 많은 부분이 동일합니다. 

하지만 동기화 처리가 빠진만큼 메서드 수행 시간이 더 빠르고, 더 효율적으로 동작합니다.

 

 

5. 응용!

그럼 내용을 다르게 수정했다가, 다시 원래대로 바꾸면, 참조 값이 다시 같아질까?

public class Test {
    public static void main(String[] args) {
        String s1 = "Hello World";
        String s2 = "Hello World";
		
        System.out.println(s1 == s2); //true
		
        s2 = s2 + "!";
        System.out.println(s1 == s2); //false
		
        s2 = "Hello World";
        System.out.println(s1 == s2); //true
    }
}

첫 번째 System.out.println(s1 == s2)는 s1과 s2가 모두 "Hello World" 문자열을 참조하고 있으므로 true를 출력합니다.

두 번째 System.out.println(s1 == s2)는 s2에 "!"를 추가한 후, s1과 비교하므로 false를 출력합니다. 이는 s2 + "!" 연산이 새로운 String 객체를 생성하고, 그 객체의 참조값을 s2가 가리키게 되기 때문입니다.

세 번째 System.out.println(s1 == s2)는 다시 s2를 "Hello World"로 초기화한 후, s1과 s2가 모두 같은 문자열을 참조하고 있으므로 true를 출력합니다.

 

+ 더 자세한 해설
Java의 String 클래스는 불변(immutable) 객체입니다. 

이는 생성된 String 객체가 변경 불가능하다는 것을 의미합니다. 

즉, 한 번 생성된 String 객체는 그 값을 변경할 수 없습니다.

이러한 불변성은 Java의 메모리 모델에서 매우 중요합니다. 

Java는 객체를 참조하는 변수가 있을 때, 해당 객체에 대한 참조 값을 변수에 저장합니다. 

불변 객체는 생성된 이후에 값이 변경될 수 없기 때문에, 이들의 값은 항상 일정합니다. 

따라서, 불변 객체에 대한 참조를 갖는 변수는 해당 객체가 가리키는 값을 변경할 수 없습니다. 

이러한 특성은 Java의 스레드 안정성(thread-safety)을 보장하는 데에도 도움을 줍니다.

하지만 이러한 불변성 때문에, String 객체를 수정하려고 하면 기존 객체를 변경하는 대신 새로운 객체를 생성합니다. 

예를 들어, String 객체에 대해 문자열 결합 연산자(+)를 사용하면, 기존 객체를 변경하는 대신, 두 개의 String 객체를 연결한 새로운 String 객체를 생성합니다. 

이 때문에 String을 연결하거나 수정하는 작업이 빈번하게 일어날 때, 메모리 소비가 높아질 수 있습니다.

그리고 불변성으로 인해 String 객체가 많은 수로 생성될 수 있다는 단점도 있습니다. 예를 들어, 문자열을 많이 연결하는 작업을 수행할 경우 새로운 String 객체가 생성될 수 있기 때문에 메모리 사용량이 늘어날 수 있습니다. 이러한 경우에는 StringBuilder나 StringBuffer 클래스를 사용하는 것이 더 효율적입니다. 이 클래스들은 가변(mutable) 객체로, 문자열을 연결할 때 기존 객체를 변경하는 대신에 내부적으로 저장공간을 조정하여 문자열을 추가할 수 있도록 해줍니다.

 

 

6. 총정리

그래서 언제 사용하는게 좋을까?!

String : 값이 자주 변하지 않을 때 (자주 변하면, 메모리 소비가 심함)

StringBuilder : 값이 변하고, 싱글스레드일때 (대신 빠름)

StringBuffer : 값이 변하고, 멀티스레드일때 (대신 느림)

 

 


이상으로, Java에서 문자열을 다루는 String, StringBuffer, StringBuilder 클래스에 대해 알아보았습니다. 

적절한 상황에서 각 클래스를 적절히 사용하면서, 보다 효율적으로 문자열을 다룰 수 있을 것입니다.

반응형

+ Recent posts