티스토리 뷰
Java에서는 문자열 객체를 생성하기 위해 String, StringBuffer, StringBuilder이라는 3가지 클래스를 제공한다.
세 클래스 모두 문자열을 관리하기 위한 클래스이지만 불변(Immutable)한지, 동기화를 지원하는지에 따라
조금씩 차이가 있다.
우선 String부터 살펴보겠다.
(String, StringBuffer, StringBuilder의 차이점에 대해 포스팅하려다 String 내용이 많아서 분리했다.)
String은 특별하다
String클래스는 다른 클래스들과는 조금 다르다. (String is Special)
Reference Type임에도 불구하고 Primitive Type처럼 사용할 수 있다.
String을 초기화할때 Primitive Type처럼 직접 문자열 리터럴(Literal)로 초기화가 가능하다.
자바에서는 연산자 오버로딩을 지원하지 않지만 String클래스에서는 특별히 '+' 연산자가 오버로딩이 되어 있어 문자열을 더할 수가 있다.
왜 이렇게 설계되었으며 무슨 이점이 있는 것일까 ?
String은 불변(Immutable)하다.
즉, 인스턴스가 생성되면 수정이 불가능하다는 뜻이다.
String의 불변성을 설명하기전에 아래의 코드결과를 살펴보자.
<noMenu/>
String s1 = "AAA";
s1 += "BBB";
System.out.println(s1);
// 출력결과 : "AAABBB"
위의 코드를 언뜻 보면 수정된 것처럼 보이지만, String의 불변성을 이해하기 위해서는
String Constant pool 영역에 대한 이해가 필요하다.
String Constant Pool
String constant pool은 Heap영역 내부에 존재하며 String객체들을 별도로 관리하는 영역이다.
먼저 아래의 코드결과를 확인해 보자.
<noMenu/>
String s1 = "Hello";
String s2 = "Hello";
String s3 = "Hello";
if(s1 == s2 && s2 == s3){
System.out.println("True");
} else {
System.out.println("False");
}
// 출력결과 : True
대상이 Reference Type일 경우, '==' 비교는 대상의 주소를 비교하는 연산자이므로 출력결과는 False가 되어야 할것 같지만 True가 결과로 출력된다.
같은 값이면 동일하게 본다는 것인데, 마치 Primitive Type을 '=='으로 비교할 때와 같다.
이것은 String 리터럴이 String Constant Pool에 저장되어 공유 데이터로써 재사용되기 때문인데
같은 문자열 리터럴이 저장된 객체들은 String Constant Pool에서 같은 리터럴 값을 가리키고 있음을 의미한다.
따라서 String은 불변하도록 설계되었으며 (공유 데이터이기 때문에 : thread-safe 관련),
String을 수정할 경우 새로운 불변의 리터럴이 String Constant Pool에 추가되는 것이다.
이로써 힙영역의 공간을 절약할 수 있으며 조금 더 효율적인 메모리 관리가 이루어 진다.
String 클래스는 2가지 초기화 방법이 있다.
<noMenu/>
// 리터럴로 초기화 하는 방식
String s1 = "Hello";
// new 연산자로 초기화 하는 방식
String s2 = new String("Hello");
s1의 "Hello"리터럴은 String Constant Pool에 공유 데이터로 저장되며,
String객체 s2는 String Constant Pool이 아닌 Heap영역에 저장이 된다.
new 연산자로 초기화 했을때 객체를 비교할 경우 리터럴값이 아닌 주소값을 비교하기 때문에
아래와 같은 결과가 나오게 된다.
<noMenu/>
String s1 = "Hello";
String s2 = "Hello";
String s3 = "Hello";
String s4 = new String("Hello");
String s5 = new String("Hello");
if(s1==s2 && s2==s3)
System.out.println("True");
else
System.out.println("False");
if(s4==s5)
System.out.println("True");
else
System.out.println("False");
/* 출력결과
True
False
*/
위의 코드를 그림으로 표현하면 이렇다.
String객체비교는 equals() 메소드를 사용하자
위의 내용을 이해했다면 String비교에 왜 '==' 연산자보다 equals() 메소드를 사용하는 것이 좋은 방법인지 알 수 있다.
초기화 방법에 따라서 기대와는 다른 비교결과가 나올 수 있으므로
String객체의 내용을 비교해 줄 수 있는 equals()을 사용하는 것이 항상 옳다.
String객체의 수정을 자주 하게 된다면 ?
계속해서 새로운 리터럴이 String Constant Pool에 생성되므로 성능이 떨어진다.
String클래스 대신 StringBuffer또는 StringBuilder클래스를 사용해야 한다.
(참고 - String, StringBuffer, StringBuilder의 차이점)
참고 링크
'개발 공부 > Java' 카테고리의 다른 글
[Java] HashMap과 HashTable의 동작 방식 (0) | 2022.02.23 |
---|---|
[Java] Object클래스 - equals, hashCode, toString 메소드 (0) | 2021.08.09 |
[Java] String, StringBuffer, StringBuilder 차이점 (0) | 2021.08.09 |
[Java] Thread-safe 클래스 (0) | 2021.08.06 |