본문 바로가기
  • 오늘도 한걸음. 수고많았어요.^^
  • 조금씩 꾸준히 오래 가자.ㅎ
IT기술/CS(ComputerScience)

[CS] java에서 String이 불변 객체인 이유

by 미노드 2024. 4. 15.

자바에서 String 문자열은 불변 객체입니다.

String으로 선언한 변수가 변하지 않는 다는 뜻에서 불변이 붙은것은 아닙니다.

String a = "num1";

a = "num2";

"num1"이라는 String 객체가 만들어지고 이를 a가 참조하는 식으로 내부에서 과정이 이루어집니다.
a="num1"을 선언하며 "num1"이 상수풀에 없으면 num1을 새로 만들고 a로 참조하도록 하는 것입니다.
다만 a ="num2" 를 수행하며 값이 변경되는 게 아닌 "num2"라는 새로운 String 객체를 만들고
타입 a에 참조시키는 것입니다.

왜 이런 구조를 띠는걸까요?

String 객체는 heap 메모리가 아닌 상수풀에 등록되고 참조를 반환합니다.
이렇게 설계된 이유는 String은 상당히 자주쓰여서 상수풀에 저장하고 같은값은 추가로 생성이 아닌 
참조로 불러와서 쓰도록 하는게 성능적인 부분에서 낫다고 해서 그렇다고 합니다.

문자열 리터럴을 캐싱하는 것이지요.

--------

String 변수 생성시 주소할당

- 리터럴 이용 : String a = "문자1";
- new 연산자 이용 : String a = new String("문자1");

리터럴을 사용하면 String 상수 풀에 생성되고, new연산자로 사용하면 Heap 영역에 생성됩니다.
String의 internal()메서드가 내부적으로 있는데, 리터럴로 생성할 경우 internal()메서드를 호출해 상수풀에 같은 값이 존재하면, 주소값을 반환하고, 없다면 상수 풀에 신규로 생성합니다.

다만 불변객체이다보니 1000번이상 수정 같은 이슈가 있을 경우
StringBuilder나 StringBuffer 로 문자열 변경 로직을 짜는것이 더 낫습니다.

---------

String이 불변객체임으로써 장점

- 멀티스레드에서 안전할 수 있다.
Thread-safe 일반적으로 불변의 개체는 동시에 실행되는 여러 스레드에서 공유해서 사용하기 유용합니다.
스레드가 값을 변경하면 동일한 문자열을 수정하는 대신 문자열 풀에 새 문자열이 생성되기 때문에 다른 스레드에서도 변경사항 적용 됩니다.

- 해시코드 캐싱(Hashcode Caching)

문자열 개체는 해시맵, 해시테이블, 해시셋 등과 같은 자료구조에서도 널리 사용됩니다.
이러한 해시 구현에 따라 작동할 때 버킷을 위해 hashCode() 메서드가 꽤 자주 호출됩니다.

public final class String {
    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;
    }
}

String의 hashCode() 메서드 구현을 보면 아직 hash 값을 계산한 적이 없을 때 최초 1번만 실제 계산 로직을 수행하고,
이후부터는 해당 값을 그냥 리턴만 하도록 overriding 되어 있습니다. (계산해놓았던 해시코드를 재사용하는 것입니다.)

String이 불변이기 때문에 이렇게 caching이 가능하다는 이점을 활용할 수 있는 것입니다. (값이 변하지 않기 때문에 위와 같이 캐싱해서 사용할 수 있는 것입니다.)