반응형

 이런 질문을 많이 받았습니다. String a와 String b가 있다면, a == b는 false이지 않나요? 사실 참조 타입에서 == 연산자는 가리키는 객체가 다르면 false를 리턴합니다. 그런데, 이 친구는 희안하게도, pool이 있습니다. 그리고 이상하게 생겨먹은 함수인 intern() 메서드가 있는데요. 자바에서 문자열을 선언했을 때, 어떤 식으로 동작하는지 이해하기 위해서는 저 둘에 대한 이해를 하시는 것이 중요합니다.

 

 


 

 먼저 6번째 줄과 7번째 줄을 보면 "str" 이런 식으로 뭔가 되어 있는 것을 볼 수 있는데요. 상수인가요? 문자열 상수는 우리는 흔히 리터럴이라고 부릅니다. 디버그 포인트를 찍어 보겠습니다.

 

 

 그러면 original에 "str"이라는 리터럴이 넘어오게 됩니다. arg0을 보면 이것의 id 값이 23이라고 되어 있어요. 그 다음에 어떤 일이 일어나는지 보면, this.value에 original.value를 넣고 있어요. value는 char형 배열인데요. 이것 또한 객체입니다. 이 value 값을 보면 id가 24로 되어 있는데요.

 

 

 이것이 그대로, 새로 생성되는 String 객체의 value에 들어갈 거에요.

 

 

 그리고 this의 id가 19라고 되어 있는데요. 이는 새로 추가된 String 객체의 id가 19라는 겁니다.

 

 

 일단 value에 new 어쩌고가 들어가지 않았기 때문에, 새로 추가된 객체의 value 필드는 이미 있는 어딘가를 참조할 듯 싶네요. 일단 여기까지 상황을 그림으로 그려보겠습니다.

 

 

 먼저 id가 23인 객체가 생성이 될 텐데요. 거기 안에는 value라는 필드가 있습니다. 이는 id가 24인, char형 배열을 가리킵니다. 그리고, 이것을 필드로 가지는 object가 어디에선가 생성이 될 겁니다.

 

 

 그런데 리터럴은 interned 된다고 설명이 되어 있는데요. 그 말인 즉슨, 이미 new String("str"); 에서 "str"이 리터럴이였기 때문에, 이 시점에서 "str"이라는 리터럴은 pool에 저장이 됩니다. 6번째 줄에서 생성자를 호출했을 때, this.value에 original.value를 집어 넣었습니다.

 

 String 생성자에서 this의 id가 19인 걸로 보아서는, 별개의 object가 생성이 된 것은 맞습니다.

 

 

 그러면 대충 이런 상황인 셈이에요. 그리고, "str" 이라는 리터럴 객체는 string pool에 들어갑니다. 즉, 보라색 부분입니다. 그리고 초록색 부분은 new로 생성이 되었기 때문에, 보라색과는 별개의 객체가 되어버립니다. new로 생성된 객체는 pool에 들어가지 않아요.

 

 

 

  String t = "str"? 이 때는 어떨까요? 요 때에는 리터럴로 선언이 되었는데요. "str"이라는 친구가 pool에 있는지 찾습니다. 보라색 부분에서 "str" 이라는 내용이 있나요?

 

 

 네. 있어요. 따라서 t는 보라색으로 칠한 객체를 가리킵니다. 이미 해당 문자열이 pool 안에 있는 경우에는, t는 pool에 있는, 패턴과 일치하는 문자열을 가리키게 됩니다. 만약에 7번째 줄이 t = "map" 이였다면 어땠을까요? 일단 "str"은 스트링 풀에 들어가 있었는데, "map"이라는 것은 아무리 눈을 씻고 찾아봐도 찾을 수 없어요.

 

 

 그러면 "map" 이라는 문자열을 pool 안에 넣을 겁니다. 그리고 풀 안에 있는 "map" 객체를 t가 가리키게 될 겁니다. 즉, 풀 안에 리터럴이 있다면, 풀 안에서 문제의 리터럴과 패턴이 일치하는 객체를 가리킵니다. 없다면, pool 안에 리터럴과 value 값이 일치하는 객체를 추가하고, t는 그 객체를 가리킬 거에요.

 

 


 

 그러면 9번째 줄의 intern은 무엇을 하는 것일까요? 문자열이 pool 안에 있으면 그 위치를 리턴해 주고, 그렇지 않다면 풀 안에 문제의 문자열 객체를 생성한 다음에, 그 위치를 리턴해 주는 함수입니다. 보시면, s = s.intern(); 이라는 문장을 수행하고 있는데요.

 

 

 이 때, "str"이라는 친구는 풀 안에 있기 때문에, s 또한 id가 23인 객체를 가리킵니다. 그러면, 당연하게도, s == t는 참 값이 되겠네요.

 

 

 아래는 문제의 프로그램을 수행한 결과입니다.

 

 


 

 예제 프로그램 2를 봅시다. 간단하게 어떤 식으로 메모리에 올라가는지 그려봅시다. 먼저 6번째 줄까지 수행했을 때, s1 = "str"; 이라는 문장이 보입니다. 리터럴이니까, "str"이라는 문자열 객체 하나가 풀에 올라갈 거에요.

 

 

 2번째는 생성자에 "batman"이라는 리터럴을 넘겨 주었습니다. 그러면, "batman"이라는 것이 pool에 들어가고, s2라는 친구는 pool 바깥에 있는, 새로 생성된 객체를 가리킬 겁니다.

 

 

 그러면 요래 되겠네요. 실제로 pool 안에 있는 "batman" 객체의 value 값이랑, s2의 value 값이랑 같다는 것은 유념해 두시면 좋겠습니다. 다음에, new String("str");이 들어왔는데요. 이미 "str" 리터럴이 있네요. 풀 안에 있네요. 그런데 new 연산자로 생성했기 때문에, 보라색으로 칠한 그것과 다른 공간에 생성이 될 거에요.

 

 

 다음에 s4 = s3.intern()이라고 되어 있어요. s3은 "str"인데요. 보라색으로 칠한 것 중에 "str"이 있는 것을 찾아 볼까요? 그러면 s1이 가리키는 객체임을 알 수 있어요. 따라서 s1 == s4임을 알 수 있어요.

 

 

 메모리 상에는 요래 저장이 됩니다. 글이 다소 길었는데요. 정리를 해 봅시다. String s = "xxx"; 이런 식으로 선언하면, "xxx" 라는 문자열 객체가 풀 안에 없다면, 풀에 새롭게 추가됩니다. 그리고 추가된 object를 s가 가리키게 됩니다. 있다면, 그 객체를 s가 가리킵니다. 이는, 요런 식으로 선언이 되었을 때, 내부적으로 "xxx"라는 것을 intern 하기 때문입니다. 이 것만 정리하시면 좋을 듯 싶습니다.

반응형

댓글을 달아 주세요

  1. 뉴엣

    어떻게 이런 것들을 다 공부하셨는지, 존경스럽습니다..
    코딩강아지님, 오늘도 좋은 하루 보내세요!