Java에서 String은 불변 객체다.
이전글 : https://riverblue.tistory.com/42?category=929398
근데 한번도 왜 불변으로 만들었는지, 불변이 뭐가 좋은지 생각해본적이 없다.
그래서 이유를 좀 찾아보게 되었고 나름대로 정리한 생각을 기록해본다.
참고 : https://www.baeldung.com/java-string-immutable
위 사이트를 보면 대략 4가지로 생각된다.
- String Pool
- 동기화
- HashCode Caching
- 보안
1. String Pool
처음 안 사실인데, 메모리의 힙 영역에 String Pool이 따로 있다고 한다. 참고한 사이트에선 Java String Pool is the special memory region where Strings are stored by the JVM 라고 표현하고 있다.
String s1 = "Hello World";
위 같은 String 객체가 있을때, Hello World를 String 리터럴 값이라고 한다. 이 리터럴 값이 String Pool에 저장되고, 해당 값을 또 선언한 경우 같은 주소값을 참조한다고 한다. 코드로 보면 이해가 편하다.
String s1 = "hello";
String s2 = "hello";
System.out.println(s1 == s2);
결과는 true.
사실 난 당연히 false가 나올줄 알았다. String은 equals로 비교해야하니까. 아래 그림을 보면 이해가 쉽다.
아마도 성능을 위해 이러한 String pool이 존재하는듯 하다.
String s3 = new String("world");
String s4 = new String("world");
System.out.println(s3 == s4);
참고로 이건 당연히 false다.
2. 동기화
멀티 스레드 환경에서 안전하다는 의미다. 불변이기 때문에 메모리에 저장된 값이 바뀔일 없다.
그래서 당연히 Thread Safe한 효과를 얻을 수 있다.
3. HashCode Caching
String은 hashCode() 메서드가 있다. 사실 개인적으로 직접 호출한 적이 없어서 몰랐는데 HashMap, HashTable, HashSet 등 아주 여기저기 자주 쓰이는 메서드라고 한다. 그래서 String을 좀 살펴보면
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final char value[];
/** Cache the hash code for the string */
private int hash; // Default to 0
.....
public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
char val[] = value;
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}
private int hash로 필드 변수가 선언되어 있고, 해시코드 캐싱을 위한 값이라고 주석처리 되어있는걸 볼 수 있다.
hashCode()는 h가 0인 경우 h에 값을 넣기 때문에 최초에만 해시값을 넣는걸 알 수 있다.
String은 불변이기 때문에 값이 바뀔일없어서 최초에 초기화된 해시값이 변하지 않을것이다. 때문에 필요할때 마다 이미 정의된 hash값으로 빠르게 사용이 되는 것이다.
4. 보안
솔직히 이 부분은 추상적으로 이해했다.
먼저 보안이라고 나타낸이유는 아무래도 중요한 데이터는 String 형태로 많이 저장하기 때문에 보안이라고 표시한듯 하다. (아이디, 암호, url 등)
참고한 사이트의 코드를 먼저 보면
void criticalMethod(String userName) {
// perform security checks
if (!isAlphaNumeric(userName)) {
throw new SecurityException();
}
// 여기 있을때 username이 바뀐다면?
// do some secondary tasks
initializeDatabase();
// 여기 있을때 username이 바뀐다면?
// critical task
connection.executeUpdate("UPDATE Customers SET Status = 'Active' " +
" WHERE UserName = '" + userName + "'");
}
위에서 주석으로 표시한 타이밍에 다른 스레드에 의해 userName값이 바뀌게 되면 그 이후의 코드에 문제가 생길 수 있다는 점이다. => initializeDatabase()가 안되거나 executeUpdate에 다른 쿼리가 되거나
특히 1번의 String Pool이 있기 때문에 같은 리터럴 값을 참조하는 String 객체가 여러개일수도 있다.
userName = "pooreun";
String some = "pooreun";
some = "zzzzz";
만약 가변이라면 some = "zzzzz"하는 순간 String Pool에 있던 "pooreun"도 "zzzzz"로 바뀌기 때문에 문제가 발생할 수 있다는 뜻이다.
'Java' 카테고리의 다른 글
StringBuilder 살짝 까보기 (생성자, append) (0) | 2022.05.06 |
---|---|
Java String CompareTo 메서드 (0) | 2021.10.14 |
Java 데이터 타입 Primitive type(기본형) Reference Type(참조형) (0) | 2021.10.13 |
Java String, StringBuilder, StringBuffer (0) | 2021.10.12 |
JVM 가비지 컬렉터(GC) Garbage Collector (0) | 2021.10.10 |