JAVA

[JAVA] 문자열 리터럴이 불변인 이유 (Why String is immutable?)

jhkimmm 2022. 1. 14. 09:34

자바를 공부하다 보면 문자열 리터럴은 불변이라는 점이 많이 강조됩니다.

String str1 = "Kim";
str1 = str1 + "Lee";

즉 위와 같은 경우에 str1에 할당된 "Kim"이라는 문자열 리터럴이 "KimLee"로 변하는 것이 아니라, str1은 새로 생성된 "KimLee"라는 문자열을 가리키도록 변하는 것입니다. 

 

String str1 = "Kim";
String str2 = "Kim";

System.out.println(str1 == str2);  // True!

※ 만약 같은 내용의 문자열을 가리키는 두 변수가 서로 다른 주솟값을 가지게 하고 싶다면

str2 = new String("Kim") 과 같이 new 연산자를 사용해서 할당해주면 됩니다.

또한 같은 문자열 리터럴 값을 가지고 있으면 두 변수는 같은 객체를 가리키고 있는 것이고 이는 == 연산자를 통해 두 변수의 주솟값을 비교해보면 쉽게 확인할 수 있습니다. 왜냐하면 자바는 문자열 리터럴을 String Pool이라는 곳에 집어 넣어서 관리하고 문자열 리터럴을 변수에 할당할 일이 있으면 String Pool을 먼저 찾아봐서 같은 값이 있으면 String Pool에 있는 객체의 주솟값을 변수에 할당해주기 때문입니다. 이렇게 String pool을 사용하면 문자열 리터럴이 할당될 때마다 객체를 새로 생성하는 것보다 메모리를 훨씬 덜 쓴다는 장점이 있습니다.

 

그러므로 문자열을 이리저리 변경할 일이 많을 때는 StringBuilder나 StringBuffer를 사용해야합니다.

저도 이 내용은 익숙하였고 암기하고 있었지만 왜 자바의 문자열 리터럴이 이런식으로 Immutable하게 설계됐는지 항상 궁금했습니다. 크게 3가지 이유가 있습니다.

첫번째, String Pool을 사용하기 위해서

조금 전의 예시를 다시 한번 살펴보겠습니다. 만약 문자열 리터럴이 변경 가능(Mutable)하다면 str1과 str2는 같은 객체를 참조하고 있기 때문에 str2의 값만 변경해도 str1의 값까지 같이 변경되어 버리는 상황이 발생될 수 있으므로 이를 방지하기 위해서 입니다.

두번째, 보안성 (Security)

pulic class ExampleClass{
	public static void main(){
    	String name = "Kim";
        
        addMoneyToAccount(name, 500000);
    }
    void static addMoneyToAccount(String account, Integer money){
    	// 계좌에 돈을 입금하는 메서드
    }

파라미터로 전달받은 이름의 계좌에 돈을 입금하는 addMoneyToAccount() 메서드가 있다고 가정하겠습니다. 문자열 리터럴이 변경 가능하다면 외부에서 String pool에 있는 문자열 리터럴 객체를 조작하여서 원하는 계좌가 아닌 곳에 입금하는 일이 생길 수 있습니다. 그러나 문자열은 불변이기 때문에 한 번 addMoneyToAccount의 파라미터인 account에 문자열 객체가 할당되면 외부에서 조작해서 값을 변경하는 것이 불가능합니다. 

세번째, Thread Safe

Thread safe란 멀티 쓰레드 프로그래밍에서 어떤 함수/변수/객체들에 여러 쓰레드가 동시에 접근하여도 프로그램의 실행에 문제가 없는 것(모든 쓰레드에서의 수행 결과가 올바른 것)입니다. 자바에서 문자열은 불변이기 때문에 수백 수천개의 쓰레드에서 동시에 접근되어도 Thread safe임을 보증합니다.