Programming/Java

String class가 final인 이유, String의 불변성 (Immutable)

Jan92 2021. 8. 24. 00:34

public final class String

 

Java의 final에 대해 공부하면서 String class가 대표적인 final class라는 것을 알게 되었고, String class가 final으로 정의된 이유가 궁금하여 찾아보게 되었습니다.

 

 


 

Java String

String str1 = new String("Hello");  // new 연산자를 통해 객체 생성
String str2 = "Hello";  // 리터럴 형태로 바로 할당

java에서 문자열(String) 타입의 특징은 참조 타입임에도 불구하고 직접 new 연산자를 통해 객체를 생성하는 방법이 아닌, 문자열 리터럴 형태로 사용하는 것이 허용된다는 것입니다.

 

 

 

String의 불변성 (Immutable)

 

String 객체는 최초 한 번 생성되면 절대로 그 값이 변하지 않습니다.

String str = "최초 문자열";
str = "변경된 문자열";

 

str 이라는 String 객체가 생성된 이후 "최초 문자열" 을 "변경된 문자열" 로 바꾼다고 해도 실제 내부적으로는 최초 생성된 String 객체의 값이 변경되는 것이 아니라 새로운 String 객체가 생성되어 그 참조가 str 변수에 할당된 것입니다.

즉, 이 상태에서는 최초에 생성된 "최초 문자열""변경된 문자열" 두 개의 인스턴스가 Heap 영역에 저장되어 있는 상태입니다.

(이후 참조되지 않는 "최초 문자열"은 Gabage Collection을 통해 제거됩니다.)

 

 

 


 

String 객체가 불변 객체(Immutable)인 이유

 

 

1. String 객체의 캐싱 기능

 

String이 불변의 객체로 설계된 이유는 메모리 절약을 위한 캐싱 기능을 위해서입니다.

Java 디자이너는 모든 종류의 Java 어플리케이션에서 가장 많이 사용되는 데이터 타입이 String이 될 것이라고 예측했습니다. 즉 String 타입의 객체들이 가장 많은 메모리를 차지할 것을 알고 있었기 때문에 처음부터 최적화의 필요성을 고려했습니다.

 

그래서 선택된 방법이 String Pool에 리터럴을 포함하여 String 객체를 공유하고, 템프러리 하게 생성된 String 객체를 줄여주는 것입니다.

이때 두 영역에서 mutable한 객체는 공유가 불가능하기 때문에 String 객체를 공유하기 위해서 String class는 Immutable class가 되어야 합니다.

 

for(int i = 0; i < 500; i++) {
    String str = "Hello World";
    System.out.println(str);
}

 

다음과 같은 코드에서 String의 불변성이 없다면 String Pool을 통한 String 객체를 공유할 수 없게 되고, 해당 코드는 "Hello World"라는 500개의 String 객체를 생성하게 됩니다.

하지만 불변의 성질을 갖기 때문에 실제로 "Hello World"라는 문자열 객체는 Heap 영역에 단 하나만 생성되어 공유됩니다.

(그러나 참조값을 갖는 참조 변수인 str 변수 자체는 각 반복문이 돌 때마다 스택상에 생성되었다가 사라질 것입니다.)

 

그리고 String 객체의 캐싱 기능은 메모리 절약과 동시에 속도가 향상되는 효과도 가지고 옵니다.

Java에서 String 객체들은 Heap의 String Pool 이라는 특별한 공간에 저장됩니다.

그리고 위 for문 처럼 참조하려는 문자열 ("Hello World")이 String Pool에 존재하는 경우 새로 생성하지 않고 Pool에 있는 객체를 사용합니다. 따라서 객체가 새로 생성되는 동작이 줄어들기 때문에 같은 문자열이 재사용되는 빈도가 높을수록 성능이 향상될 수 있습니다.

 

 

2. 보안상의 이유

 

String은 다수의 Java 클래스에 매개 변수로 널리 사용되고 있으며, 네트워크를 연결할 때 호스트 및 포트가 String으로 되어있고, Java에서 파일을 읽어 들이기 위한 파일이나 디렉토리 경로도 String으로 되어있으며, 데이터베이스 연결에 필요한 URL도 역시 String으로 되어있습니다.

이 상황에서 만약 String이 immutable 하지 않다면, 사용자는 시스템의 특정 파일에 대한 엑세스 권한을 얻은 후 Path의 변경이 가능하게 되며, 이렇게 되었을 경우 심각한 보안 문제가 발생할 수 있습니다.

 

class loading mechanism에서 자주 사용되는 것도 이유 중 하나입니다. String 객체의 값이 변할 수 있다면 java.io.Reader 등 Java 표준 클래스의 로드 요청 시 악성 com.unknown.DataStolenReader 클래스로 변경하게 할 수 있습니다.

String을 final으로 지정함으로써 JVM은 올바른 클래스들을 로드할 수 있게 됩니다.

 

 

3. Hash

 

또한 문자열은 HashMap, Hashtable 같은 해시 기반 컬렉션의 키로써 많이 사용되기 때문에 동일한 값으로 저장된 객체의 value를 검색할 수 있도록 immutable한 것이 중요합니다. 키로 지정된 문자열의 내용이 수정된 경우 삽입 및 검색 시 두 개의 다른 해시 코드를 생성하여 잠재적으로 맵에서 값 객체를 잃게 됩니다.

 

 

 


 

* 혼동하지 말아야 할 개념

 

  • 불면성의 개념, 문자열을 재할당할 때 해당 문자열을 참조하는 객체를 수정하는 것이 아니라 새 문자열을 만들고, 변경된 문자열을 할당한다는 것.
    (즉, String은 한 번 생성되면 변경할 수 없으며, 대신 새로운 String 객체가 생성됩니다.)
  • 문자열이 변경 불가능하다는 것은 객체 자체는 변경할 수 없지만 객체에 대한 참조는 변경할 수 있다는 의미입니다.

 

 

 

* 함께 보면 좋은 글

https://wildeveloperetrain.tistory.com/33

 

Static, Stack, Heap / Java 메모리 영역의 구조와 특징

Java를 사용하면서 알아야 할 메모리 구조 및 특징에 대해서 정리합니다. (Static, Stack, Heap) 먼저 프로그램을 구동하기 위해서는 운영체제(OS)가 메모리(RAM)에 데이터 및 명령어를 저장할 공간을 할

wildeveloperetrain.tistory.com

 

 

* 참고 사이트

https://www.mimul.com/blog/why-string-class-has-made-immutable-or-final-java/

https://dololak.tistory.com/699

https://javarevisited.blogspot.com/2010/10/why-string-is-immutable-or-final-in-java.html#axzz74Jy8VeMT