String을 선언하는 방법은 아래와 같이 두가지다.
String str1 = new String("abc");
String str2 = "abc";
String str1 = new String("abc");
String str2 = new String("abc");
System.out.println(str1 == str2);//false
String str3 = "abc";
String str4 = "abc";
System.out.println(str3 == str4); //true
힙 메모리는 생성될 때 마다 다른 주소값을 가진다.
따라서 ==
연산자를 사용해 str1과 str2를 비교하면 false
가 나온다.
반면 str3과 str4처럼 상수 풀의 문자열은 모두 같은 주소값을 가진다.
한번 생성된 String은 불변 immutable
의 특징을 가지고 있다.
예를 들어 concat()
메소드를 사용해서 String 과 String을 연결하면
기존의 문자열에 내용이 추가/연결되는 것이라고 생각하기 쉽지만
사실은 이전 문자열과 다른 아예 새로운 문자열이 생성된다.
public class StringTest {
public static void main(String[] args) {
String java = new String("java");
String android = new String("android");
System.out.println(System.identityHashCode(java));
java = java.concat(android);
System.out.println(System.identityHashCode(java));
System.out.println(java);
}
}
"Fast"와 "Campus" 문자열을 더하면 "Fast Campus"가 되는듯 싶지만
위 코드처럼identityHashCode()
를 사용해 객체의 고유한 값을 리턴한 결과를 보면
사실은 "Fast Campus"라는 새로운 문자열이 생성된 것이고, 변수는 그것을 가르킬 뿐이라는 것을 알 수 있다.
따라서 이렇게 String 연결을 다건하게 되면 메모리 낭비가 발생할 수도 있다.
그러면 어떻게 해야 하느냐?
이럴 때 사용하기 좋은 것이 StringBuilder, StringBuffer 클래스 이다.
final로 선언된 String과 달리 StringBuilder, StringBuffer는
내부적으로 char[]
를 멤버 변수로 가져 값이 가변적이다.
새로운 인스턴스를 생성하지 않고 char[]
를 변경하는 것이다.
해당 값을 사용할 때는 toString() 메서드로 String 반환을 하면 된다.
이런 특징 때문에 문자열을 여러번 연결하거나 변경할 때
StringBuilder, StringBuffer를 사용하면 유용하다.
StringBuilder, StringBuffer 모두 char[]
를 멤버 변수로 가졌다는 공통점이 있지만,
StringBuffer는 멀티 쓰레드 프로그래밍에서 동기화(synchronization)을 제공한다는 차이점이 있다. (두개 이상의 쓰레드가 서로 같은 멤버를 Access할 때 순서가 정해진다는 이야기)
따라서 멀티 쓰레드 프로그램에서는 StringBuffer를 사용하고,
단일 쓰레드 프로그램에서는 StringBuilder 사용을 권장한다.
위 String 예제에서 사용한 코드와 동일한 예시로
둘 중 StringBuilder 에 대해 더 자세히 알아보자.
public class StringBuilderTest {
public static void main(String[] args) {
String java = new String("java");
String android = new String("android");
StringBuilder buffer = new StringBuilder(java);
System.out.println(System.identityHashCode(buffer));
buffer.append(android);
System.out.println(System.identityHashCode(buffer));
String test = buffer.toString();
System.out.println(test);
}
}
String 과 달리, identityHashCode()
를 사용해도 동일한 값이 리턴됨을 알 수 있다.
위 예시처럼 반환 시에는 toString()
을 사용하면 된다.
그리고 자바 13 버전에서부터 제공되는 text block 을 사용하면
문자열을 """ """ 사이에 이어서 긴 텍스트를 쉽게 만들 수 있고
html, json 문자열을 만드는데에도 유용하게 사용할 수 있다.
public class TextBlockTest {
public static void main(String[] args) {
String textBlock = """
Hmm... This is text block test
Interesting. """;
System.out.println("textBlock = " + textBlock);
System.out.println(getBlockOfHtml());
}
public static String getBlockOfHtml() {
return """
<html>
<body>
<span>example text</span>
</body>
</html>""";
}
}