String 클래스의 구현 문제
- Posted at 2006/01/10 12:57
- Filed under Program/JAVA
동일한 String 타입을 가진다 하더라고 문자열 리터럴과 객체로 생성된 문자열은 메모리 상에서 다르게 위치하게 된다. 문자열 리터럴로 생성된 String 타입은 동일한 문자열이 하나만 존재하게 된다. 다음 그림은 String이 리터럴에 의한 생성이냐 객체로 인한 생성에 따른 메모리 배치 구조이다.

이런 구조에서 다음과 같은 "==" 처리는 false가 된다.
String a = " ";
String b = new String(" ");
System.out.println(a == b);
객체의 "==" 연산은 두 객체가 가지고 있는 값에 대해서 비교를 하는 것이 아니라 동일한 메모리를 참조하는지 여부만을 판단하기 때문이다.
이런 경우는 어떻게 될까?
이 경우도 false를 출력한다. 이유는 String 클래스의 메소드 substring(), trim()을 수행시킨 결과는 새로운 String 클래스로 반환되기 때문이다.
하지만 다음의 경우에는 이와 다르게 동작한다.
이것은 true를 출력한다. 똑같은 소스임에도 String a의 값에 따라 다른 결과가 나타나는 것이다. 이것은 개발자에게 상당히 혼란스럽다.
JDK1.4의 String 구현 소스를 보면 다음과 같이 구현되어 있다.
public String trim() {
...
return ((st > 0) || (len < count)) ? substring(st, len) : this;
}
public String substring(int beginIndex, int endIndex) {
...
return ((beginIndex == 0) && (endIndex == count)) ? this :
new String(offset + beginIndex, endIndex - beginIndex, value);
}
이러한 String의 구현은 substring() 또는 trim()을 수행해야할 필요가 있을 때만 new String() 에 의해 새로운 객체가 생성되고 위의 예제와 같이 ""를 trim() 하는 경우 굳이 trim()을 수행시킬 필요가 없기 때문에 return this가 수행되는 효과를 가진다.
따라서 동일한 메모리를 참조하는 값을 반환하다보니 결과는 true가 되는 것이다.
필자의 생각에는 이것은 잘못된 구현이라 생각한다. 언어의 스펙상 당연히 false가 되어야 함에도 불구하고 구현에서 이를 무시하였기 때문이다. 하지만 대부분의 JDK가 이렇게 구현되어 있는 상황에서는 개발자는 객체의 비교 특히 String에 대한 비교를 할 경우에는 반드시 equals() 메소드를 이용해야 할 것으로 생각된다.
* 앞의 String 메모리 구조에서 객체 영역에 있는 String 을 literal 영역으로 보내기 위해서는 intern() 메소드를 이용하면 된다.
String a = "123";
String b = new String("123");
System.out.println(a == b.intern());
이것은 true 이다.

이런 구조에서 다음과 같은 "==" 처리는 false가 된다.
String a = " ";
String b = new String(" ");
System.out.println(a == b);
객체의 "==" 연산은 두 객체가 가지고 있는 값에 대해서 비교를 하는 것이 아니라 동일한 메모리를 참조하는지 여부만을 판단하기 때문이다.
이런 경우는 어떻게 될까?
System.out.println("" == a.trim());
이 경우도 false를 출력한다. 이유는 String 클래스의 메소드 substring(), trim()을 수행시킨 결과는 새로운 String 클래스로 반환되기 때문이다.
하지만 다음의 경우에는 이와 다르게 동작한다.
String a = "";
System.out.printlh("" == a.trim());
System.out.printlh("" == a.trim());
이것은 true를 출력한다. 똑같은 소스임에도 String a의 값에 따라 다른 결과가 나타나는 것이다. 이것은 개발자에게 상당히 혼란스럽다.
JDK1.4의 String 구현 소스를 보면 다음과 같이 구현되어 있다.
public String trim() {
...
return ((st > 0) || (len < count)) ? substring(st, len) : this;
}
public String substring(int beginIndex, int endIndex) {
...
return ((beginIndex == 0) && (endIndex == count)) ? this :
new String(offset + beginIndex, endIndex - beginIndex, value);
}
이러한 String의 구현은 substring() 또는 trim()을 수행해야할 필요가 있을 때만 new String() 에 의해 새로운 객체가 생성되고 위의 예제와 같이 ""를 trim() 하는 경우 굳이 trim()을 수행시킬 필요가 없기 때문에 return this가 수행되는 효과를 가진다.
따라서 동일한 메모리를 참조하는 값을 반환하다보니 결과는 true가 되는 것이다.
필자의 생각에는 이것은 잘못된 구현이라 생각한다. 언어의 스펙상 당연히 false가 되어야 함에도 불구하고 구현에서 이를 무시하였기 때문이다. 하지만 대부분의 JDK가 이렇게 구현되어 있는 상황에서는 개발자는 객체의 비교 특히 String에 대한 비교를 할 경우에는 반드시 equals() 메소드를 이용해야 할 것으로 생각된다.
HP환경에서는 JDK1.3과 JDK1.4에서 String a = " ", String b = a.trim()에서 "" == b 의 결과가 다르게 나타난다고 하는데 이것은 HP의 경우 JDK.13.과 JDK.1.4에서의 String 구현이 틀리기 때문이다.
* 앞의 String 메모리 구조에서 객체 영역에 있는 String 을 literal 영역으로 보내기 위해서는 intern() 메소드를 이용하면 된다.
String a = "123";
String b = new String("123");
System.out.println(a == b.intern());
이것은 true 이다.
Posted by 김형준
- Response
- No Trackback , No Comment
Trackback URL : http://www.jaso.co.kr/trackback/42






