String
string은 불변이다.
String str = "abc";
str = "def";
가 가능한데 왜 불변일까.
여기서 중요한 점은, String은 기본타입이 아닌 참조타입이라는 점이다.
String 변수형의 첫 글자가 대문자임에서도 나타나듯이, 클래스이다.
String 객체는 stack영역에 직접 값을 저장하는 int, char등과 달리
Heap영역의 String constant pool이라는 곳에서 따로 관리된다.
드래그
List<int>가 안되고 List<Integer>은 되는 이유는, wrapper는 클래스이기 때문에 힙영역에서 관리된다.
collection 역시 힙 영역에서 관리되기 때문에, List<int>는 힙영역에서 스택영역을 참조하는 구조라 말이 안된다.
(방금 생각한 내용)
int num = 10;
num = 20;
기본 타입인 int는 값 자체가 스택 메모리에 저장된다.
이후 값이 바뀌면 10이라는 것을 20으로 바꾼다.
따라서 가변이라 할 수 있다.
하지만 String은 참조 타입이기 때문에 Heap영역의 String constant pool에 값을 가지고 그 주소를 stack영역에서 가지게 된다.
값이 "abc"에서 "def"로 변경되면, 새로운 "def"를 만들고 그 주소값을 참조하게 된다.
따라서 str에 새로운 값을 넣는다 해도 "abc"의 값이 "def"로 바뀌는 게 아니다라는 말이다.
"abc"값은 그대로 유지된다.
따라서 불변 이라는 것이다.
이렇게 String을 불변으로 관리하면 아래의 이점을 가질 수 있다.
1. Heap영역의 메모리를 절약할 수 있다. 같은 값을 가지는 String들에 대해 같은 메모리를 참조하게 할 수 있기 때문이다.
2. 멀티 쓰레딩 환경에서 동기화 문제에 대해 안전하다.
3. JAVA는 String의 hashchode를 생성할 때 캐싱한다. 따라서 쓰일 때 마다 hashcode를 계산하지 않아도 된다.
hashcode를 키값으로 사용하는 HashMap에서 좋은 성능을 발휘한다.
4. String이 불변이 아니라면 String으로 관리되는 여러 데이터에 대해 보안상의 문제가 생길 수 도 있다.
여기서 주의할 점은 String을 생성하는 방법은 두가지가 있는데,
String str1 = "apple";
String str2 = new String("apple");
위의 두 변수의 결과는 동일하지만 저장되는 공간은 엄연히 다르다.
String literal로 생성하면 문자열은 힙영역의 String Pool에 저장되어 만약 문자열이 같으면 참조를 공유할 수 있다. (같은 객체이다)
new 연산자로 생성하면 문자열은 힙영역에 저장되어 문자열이 같더라도 새로운 주소에 새로 생성되기 때문에 공유할 수 없다. (다른 객체이다)
StringBuilder
String에서 기존의 문자열에 문자열을 추가하는 방법으로 + 연산자를 사용 할 수 있다.
하지만 String은 불변성이 있으므로 + 연산을 하면 다음과 같은 일이 발생한다.
String str = "abc";
str += "def";
"abcdef"라는 새로운 값을 가진 메모리 영역이 생기고 그를 참조하게 바뀌게 된다.
따라서 + 연산이 발생할 때 마다 새로운 메모리를 할당하게 되고, 상당한 성능저하를 일으킨다.
여기서 대체할 수 있는 방안으로 StringBuilder가 있다.
Oracle Javadoc에는 StringBuilder를
mutable sequence로 정의하고 있다.
StringBuilder sb = new StringBuilder("abc");
sb.append("def");
String str = sb.toString();
sb객체는 생성될 때 "abc"라는 메모리에 할당하는 것이 아니라 버퍼에 가지고 있다.
그리고 append가 발생할 때 마다 버퍼의 끝에 문자열을 추가한다.
마지막으로 toString() 메소드를 통해 버퍼에 저장되어 있는 최종 값을 String으로서 메모리에 할당하는 것이다.
StringBuilder를 만드는 방법은
StringBuilder 변수이름 = new StringBuilder(문자열);
또한 StringBuilder로는 아래의 작업들을 할 수 있다.
sb.append(문자열); // 문자열의 끝에 새로운 문자열 추가
sb.insert(int index, String value); // index의 위치에 value 문자열 삽입
sb.remove(int index, int length); // index위치부터 length만큼 제거
sb.replace(String oldValue, String newValue); // oldValue문자열을 newValue문자열로 교체
sb.toString(); // String으로 바꿈
'Java' 카테고리의 다른 글
static 메소드 사용 (0) | 2020.12.09 |
---|---|
equals() Overriding (0) | 2020.12.09 |
Arrays.asList() (0) | 2020.12.06 |
Google Java Style Guide(번역) (0) | 2020.11.28 |
Java 접근지정자 (0) | 2020.11.08 |