자바는 문자열을 사용할 수 있도록 String 클래스를 제공한다.
String을 사용하는 방법은 두 가지가 있는데,
String str1 = new String("abc"); 처럼 생성자의 매개변수로 문자열을 생성하는 방식과
String str2 = "test"; 처럼 문자열 상수를 가리키는 방식이 있다.
두 가지는 큰 차이가 있다.
new 예약어를 사용하여 객체를 생성하는 경우는 "abc" 문자열을 위한 메모리가 할당되고 새로운 객체가 생성된다.
str2 = "test"와 같이 문자열 상수를 가리키는 경우에는 "test"라는 문자열 상수의 메모리 주소를 가리키게 된다.
따라서 String str3 = "test" 코드를 작성하면 str2와 str3는 주소 값이 같게 된다.
그림으로 나타내면 다음과 같고, test나 10, 20 등과 같이 프로그램에서 사용되는 상수 값을 저장하는 공간을
'상수 풀'이라고 한다.
public class StringTest {
String str1 = new String("abc");
String str2 = new String("abc");
System.out.println(str1 == str2); // 인스턴스가 매번 새로 생성되므로 주소 값이 서로 다르므로 false
System.out.println(str1.equals(str2)); // 문자열 값은 같으므로 true
String str3 = "abc";
String str4 = "abc";
System.out.println(str3 == str4); // 문자열abc는 상수풀에 저장되어 있으므로 str3,str4가 가리키는 주소 같음
System.out.println(str3.equals(str4)); // 문자열 값도 같으므로 true 반환
}
위 코드를 보면 str1 == str2 는 인스턴스가 각각 생성되어서 주소 값이 서로 다르므로 false가 출력되고,
str1.equals(str2) 는 문자열 값을 비교해서 같으므로 true가 출력,
str3 == str 4 는 문자열 "abc"가 상수 풀에 저장되고, "abc"를 str3, str4가 같이 가리키므로 주소는 같아서 true 출력,
str3.equals(str4) 는 문자열 값 비교해서 같으므로 true가 출력된다.
이렇게 생성된 문자열들은 변경되지 않고 이것을 '문자열은 불변한다'라고 한다.
그렇다면 프로그램에서 두 개의 문자열을 연결하면 어떻게 될까???
이 경우에는 하나의 문자열이 변경되는 것이 아니라 두 문자열이 연결된 새로운 문자열이 생성된다.
public class StringTest {
public static void main(String[] args) {
String a = new String("abc");
String b = new String("def");
System.out.println(a);
System.out.println("처음 문자열 주소 값 : " + System.identityHashCode(a));
a = a.concat(b);
System.out.println(a);
System.out.println("연결된 문자열 주소 값 : " + System.identityHashCode(b));
}
}
문자열은 불변하므로 a 변수값 자체가 변하는게 아니라 새로운 문자열이 생성된 것이다.
abcdef 문자열이 새로 생성되고 a는 그 문자열을 가리키게 된 것이고, 처음 해시코드값과 문자열을 연결한 후 해시코드값을 비교해보면 주소 값이 달라진 것을 알 수 있다.
그렇다면 문자열은 어떻게 변경해야하나 ???
String 클래스는 한번 생성되면 그 내부의 문자열이 변경되지 않기 때문에 String 클래스를 사용하여 문자열을 계속 연결하거나 변경하는 프로그램을 작성하면 메모리가 많이 낭비된다.
이 문제를 해결하는 것이 바로 StringBuffer와 StringBuilder 클래스가 있다.
이 두 클래스를 사용하여 문자열을 연결하면 기존에 사용하던 char[] 배열이 확장되므로 추가 메모리를 사용하지 않는다.
따라서 문자열을 연결하거나 변경할 경우 두 클래스 중 하나를 사용하면 된다.
두 클래스의 차이는 여러 작업(스레드)이 동시에 문자열을 변경하려 할 때 문자열이 안전하게 변경되도록 보장해 주는가 그렇지 않은가의 차이다.
StringBuffer 클래스는 문자열이 안전하게 변경되도록 보장하지만, StringBuilder 클래스는 보장되지 않는다.
따로 스레드를 생성하는 멀티스레드 프로그램이 아니라면 StringBuilder가 실행속도가 더 빠르다.
package object;
public class StringBuilderTest {
public static void main(String[] args) {
String javaStr = new String("Java");
System.out.println("javaStr 문자열 주소 : " + System.identityHashCode(javaStr));
StringBuilder buffer = new StringBuilder(javaStr);
System.out.println("연산 전 buffer 메모리 주소 : " + System.identityHashCode(buffer));
buffer.append(" and");
buffer.append(" android");
buffer.append(" programming is fun!!");
System.out.println("연산 후 buffer 메모리 주소 : " + System.identityHashCode(buffer));
javaStr = buffer.toString();
System.out.println(javaStr);
System.out.println("연결된 javaStr 문자열 주소 : " + System.identityHashCode(buffer));
}
}
위 코드에서 StringBuilder 클래스를 생성하고 여기에 문자열을 추가했다. 그러면 append()가 실행될 때마다 메모리가 새로 생성되는 것이 아니고, 하나의 메모리에 계속 연결되는 것을 해시 코드 값을 통해 알 수 있다. 연산 전 메모리 주소와 연산 후 메모리 주소가 같기 때문에. 그리고 문자열을 변경한 후 buffer에 toStrint()을 호출하면 다시 문자열로 반환할 수 있다.
'자바 기초' 카테고리의 다른 글
자바 Class 클래스 - Do it! 자바프로그래밍기초 (0) | 2022.07.26 |
---|---|
자바 clone() 메서드 - Do it ! 자바프로그래밍기초 (0) | 2022.07.25 |
자바 hashCode() 메서드 - Do it ! 자바프로그래밍 기초 (0) | 2022.07.25 |
자바 Object 클래스, toString, equals - Do it! 자바프로그래밍기초 (0) | 2022.07.25 |
자바 인터페이스 상속 - Do it! 자바프로그래밍 기초 (0) | 2022.07.22 |