본문 바로가기
Baeldung번역&공부/Java-string

왜 String은 불병성을 가지나(Why String Is Immutable in Java?)

by ms727 2025. 1. 21.

원본: https://www.baeldung.com/java-string-immutable

 

원본글을 기반으로 번역 + 추가 테스트 코드, 의견 등을 작성한 글입니다.

 

인터뷰 면접 질문에서 "왜 Java에서 String은 불변성을 지니나요?"라고 물어볼 때가 있습니다.

Java 창시자 고슬링은 "가능할때마다 불변성을 사용하겠다"라고 말할정도로 불변성이 가진 이점이 많은데 그 이유를 알아보겠습니다.

크게 캐싱, 보안, 재사용성 등을 말할 수 있는데, 그 전에 불변성이 무엇인지 알아보겠습니다.

1. 불변성 객체?

불변성 객체란 생성된 이후로 내부 상태값이 일정하게 유지되는 객체를 뜻합니다.
즉슨, '객체가 할당된 이후 해당 값을 변경할 수 없음'을 뜻합니다.

일단 이정도로 알아가고 불변성에 대한 이야기는 다른 잘 정리된 블로그글을 참고하시면 될 것 같습니다.

 

2. String Pool

 

String Pool이란 String객체가 저장되어있는 특별한 메모리공간입니다. 이 공간을 통해서 JVM은 literal String을 pool에 저장하여 재사용하거나 불변성을 제공하고 있습니다.

 

```Java

@Test
public void 리터럴_String은_같은메모리를_참조한다() {
    //given
    String s1 = "minseok";
    String s2 = "minseok";

    //when

    //then
    Assertions.assertTrue(s1 == s2);
}

```

 

위 테스트가 성공하는 이유는 자바 메모리구조가 다음과 같기 때문입니다.

 

 

동등성, 동일성 관련한 내용이라 관련 내용에 대한 숙지가 부족하시다면 한 번 찾아보시는게 좋을 것 같습니다.

 

이러한 메모리 구조덕분에 힙 영역을 좀 더 효율적으로 쓸 수 있는것입니다.

 

3. Security

 

String이 불변객체이기에 보안쪽으로 유리한 장점도 있습니다.

 

 

먼저 다음 간단한 예제를 확인해보시죠.

 

    @Test
    public void String불변객체확인() {
        //given
        String s1 = "minseok";

        //when
        changeStringToAlice(s1);
        
        //then
        assertTrue(s1.equals("minseok"));
    }

    private void changeStringToAlice(String s1) {
        s1= "Alice";
    }

 

 

changeStringToAlice()함수에서 받은값을 무조건 "Alice"로 바꾸어도 이를 호출한 함수쪽의 변수에는 바뀌지 않습니다.

 

이를 기준으로 밑의 예제를 보시죠,

 

void criticalMethod(String userName) {
    // perform security checks
    if (!isAlphaNumeric(userName)) {
        throw new SecurityException(); 
    }
	
    // do some secondary tasks
    initializeDatabase();
	
    // critical task
    connection.executeUpdate("UPDATE Customers SET Status = 'Active' " +
      " WHERE UserName = '" + userName + "'");
}

 

이 코드는 userName을 외부에서 받아서 사용하고 있습니다. 간단한 예외처리도 진행되고 있습니다.

만약 String이 가변객체였다고 가정하고, initializeDatabase() 메서드에서가 userName을 인자로 받고, 그 안에서"userName=  "Alice' OR '1'='1' " 이런 내용이 있었다고 하면 이는 밑의 update구문에서 심각한 문제를 발생시켰을겁니다.

 

불변성은 "String 내용 자체"가 바뀌는 것을 방지하지만, 참조 변경을 막아주지는 않습니다. 따라서 코드에서 final 키워드를 사용하거나, 불필요한 참조 변경이 일어나지 않도록 유의해야 합니다

 

 

4. 동기화

 

다른 예시로 동기화 문제가 있습니다. String이 불변성을 지니기에 멀티쓰레드 환경에서도 안전할 수 있습니다.

왜냐하면 만약 하나의 쓰레드에서 값을 변경하였다면 기존 객체를 수정하는 것이 아닌, 새로운 객체를 String Pool에 생성하고 할당하여 그 값을 참조하게끔 하기 때문입니다.

 

    @Test
    void stringIsThreadSafe() {
        // given
        String sharedString = "immutableString";
        AtomicBoolean isConsistent = new AtomicBoolean(true);

        // when
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        for (int i = 0; i < 10; i++) {
            executorService.execute(() -> {
                // Simulate concurrent access
                if (!sharedString.equals("immutableString")) {
                    isConsistent.set(false);
                }
            });
        }

        // 실행객체 종료
        executorService.shutdown();
        while (!executorService.isTerminated()) {
            // 쓰레드 종료를 기달린다.
        }

        // then
        assertTrue(isConsistent.get(), "String is not thread-safe!");
    }

 

sharedString이 불변하다는것을 전제로 10개의 스레드에서 해당값을 읽고 수정되었을때 실패하게끔 하는 테스트입니다.

 

 

테스트가 성공이라는것은 String이 멀티쓰레드환경에서 안정성을 보장한다는 것입니다.

 

5. 해시코드 캐싱

 

String은 불변성을 가지고 있기에 해시코드값도 동일하다는 내용이기에 Map같은 자료구조에서도 캐시용으로 쓰인다는 내용입니다.

 

        HashMap<String, String> immutableMap = new HashMap<>();
        String key = "immutableKey";
        immutableMap.put(key, "immutableValue");

        // Attempt to modify the key (not possible since String is immutable)
        key = "modifiedKey";

        System.out.println("Immutable Map Retrieval:");
        System.out.println("Value for 'immutableKey': " + immutableMap.get("immutableKey")); // Works

 

 

이런 코드가 있고 HashMap의 get()메서드가 어떻게 구현되어있는지 확인해봅니다.

 

이렇듯 순차적으로 보면 결국 Object객체의 hashCode()를 보고있는데, LiteralString기준으로 hashCode는 불변하므로 이를 기준으로 빠르게 캐싱된 값을 찾는것을 확인할 수 있습니다. 

 

6. 성능

 

위에서 설명한대로 String Pool의 존재덕에 메모리 효율성이 늘어나고 빠르게 자원에 접근이 가능합니다. 

따라서 String을 잘 고려해서 쓰면 더욱 메모리를 효율적으로 쓸 수 있을것입니다.

 

7. 결론

이 글을 통해서 String이 지닌 불변성을 확인할 수 있었고 이 덕에 다른 메서드에서 값을 변경한다해도 원본값은 변경되지 않고, 멀티 쓰레드환경에서도 안전하게 동작할 수 있다는걸 알 수 있었습니다.

 

또한, 애플리케이션을 제작할 때 불변성을 고려해야한다는 것도 확인할 수 있었습니다.