자바에서 String 은 불변객체(immutable instance)입니다. 불변객체라 함은 한번 생성되면 그 내용을 바꿀 수 없다는 뜻입니다. 때문에 사용상 주의해야 할 점들이 다소 존재합니다. 예를 들어서

String hello = "Hello";

라는 문장이 있다고 칩시다. 이 문장에서 일어나는 일은 우선 "Hello" 라는 문자열 인스턴스(String instance)를 메모리에 생성합니다. 그리고 그 레퍼런스(참조자)를 hello 라는 문자열 변수에 대입합니다. hello 는 "Hello"라는 문자열 자체는 아닙니다. 자바에서 모든 객체들이 그렇듯 단지 hello 는 "Hello" 라는 문자열 객체를 가리키는 레퍼런스를 담고 있을 뿐이죠. (C의 포인터와 비슷한 개념이라고 보시면 무난합니다. 비슷한 개념일뿐 같은 개념은 아니므로 포인터 연산 같은걸 하실 순 없겠지요? ^^;)

자 다음으로 이런 문장이 또 있다고 해보겠습니다.

hello += " and how are you?";

이 문장을 수행한 결과 hello 가 가리키는 문자열 객체의 내용은 "Hello and how are you?" 가 될 것입니다. 분명히 위에서 말하길 String 은 불변객체라 했고, 그 내용을 바꿀 수 없다고 했는데, 왜 내용이 바꼈냐? 라고 물어보실 수 있을 거 같네요.

어떤일이 일어났는지 살펴보겠습니다.

hello += " and how are you?" 라는 문장은 내부적으로

hello = new StringBuffer(hello).append(" and how are you?").toString();

으로 변환 됩니다. 왜 이렇게 하느냐 하면 String 객체 자체는 불변입니다. 때문에 그 내용을 바꿀 방법이 없습니다.

좀 풀어서 설명을 하면 이렇습니다.

1. " and how are you?" 라는 문자열 객체를 하나 생성합니다.
2. StringBuffer 객체를 하나 생성하는데 이 때 내용은 hello가 가리키는 내용 즉 "Hello"로 채웁니다.
3. StringBuffer 객체의 내용에다 " and how are you?" 라는 문자열을 덪붙입니다.
4. 그 결과를 문자열로 변환하고, 변환된 문자열 객체의 레퍼런스를 hello 에 담습니다.

이 과정에서 " and how are you?" 라는 문자열 객체와, "Hello" 라는 문자열 객체 두 개가 garbage 화 됩니다. (레퍼런스가 사라지므로..) 그리고 StringBuffer 하나가 생성되어 처리한 후 garbage 화 됩니다. 총 3개의 garbage 가 생성이 됐네요. 나중에 얘들은 Garbage Collector 에 의해 일정 시간후 소거될 것입니다. 즉, "Hello" 라는 문자열 자체가 수정된 것이 아니라, "Hello and how are you?" 라는 새로운 문자열 객체가 생성되어 hello 에 그 레퍼런스가 대입된 것입니다.

그럼 이런걸 한번 생각해볼까요?

String mySQL = "SELECT ";
mySQL += "name, ";
mySQL += "address ";
mySQL += "FROM mytable ";
mySQL += "WHERE name = ?";

이건 어떻게 변환될까요?

String mySQL = "SELECT ";
mySQL = new StringBuffer(mySQL).append("name, ").toString();
mySQL = new StringBuffer(mySQL).append("address ").toString();
mySQL = new StringBuffer(mySQL).append("FROM mytable ").toString();
mySQL = new StringBuffer(mySQL).append("WHERE name = ?").toString();

이런 식으로 변환됩니다. 딱 보기에도 엄청난 삽질이 아닐 수 없지요? 엄청난 수의 StringBuffer 와 임시 문자열 객체들이 생성됩니다. 객체를 생성하는것 자체가 가벼운 작업이 아니므로 퍼포먼스 또한 나빠지게 되고, 덤으로 메모리도 효과적으로 사용하지 못합니다.

그래서 보통 이런 녀석들을 처리할때는

StringBuffer myBuf = new StringBuffer("SELECT ");
myBuf.append("name, ");
myBuf.append("address ");
myBuf.append("FROM mytable ");
myBuf.append("WHERE name = ?");
String mySQL = myBuf.toString();

이렇게 합니다. 임시적으로 생성되는 StringBuffer 를 거의다 없앴고, 임시적으로 생성되는 문자열(toString() 메소드에 의해)도 다 제거했습니다.

StringBuffer 는 내부적으로 String 객체가 아닌 char[] (문자 배열)을 사용하기 때문에, 내용의 추가 및 삭제, 변환등이 자유롭습니다. 한마디로 조작 가능한 문자열인 샘이죠. String 의 경우 immutable 한 특성때문에 어떤 변환(substring, replace 등)을 할 경우, 새로운 객체를 만들어 내게 되고, 이전 객체는 더이상 레퍼런스가 없다면 버려지게 됩니다.

이런이유로 StringBuffer 를 사용하는 것이고, 문자열의 조작을 원한다면 StringBuffer 를 사용하시는게 답이 되겠죠. 다만 귀차니즘에 의거하여 String 더하기를 사용하기도 합니다만, 그 부분이 프로그램상에서 많이 불려지는 부분이라면 최적화 차원에서 후에 StringBuffer 로 변환해 주시는 편이 좋습니다.

뭐 쓸데없이 주절주절 말이 많았는데요, 정리해 보자면 StringBuffer 는 문자열을 조작하기 위한 Helper 클래스라고 보시면 되구요. 잊지 말아야 할 사실은 String 은 불변객체이므로 String 자체를 조작하는 것은 상당히 비싼 작업이다 라는걸 기억하시면 될 것입니다.

도움이 되셨길 바랍니다
결국 가비지객체를 만들지 않기 위해 StringBuffer를 사용한다.
Posted by euNey^0^

댓글을 달아 주세요

  1. BlogIcon 과로인생 2008.06.08 19:59 신고 Address Modify/Delete Reply

    오옷 오래간만의 포스팅이네요 -ㅅ-
    잘 지내십니까?

티스토리 툴바