[DEV] J-Jay

String 본문

Back-end/Java

String

J-Jay 2023. 4. 23. 15:21
728x90
String은 왜 final로 선언되어 있을까?
public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence,
               Constable, ConstantDesc {
String
String str1 = new String("Hello");  // new 연산자를 통해 객체 생성
String str2 = "Hello";  // 리터럴 형태로 바로 할당

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

 

String의 불변성 (Immutable)

String은 최초에 한 번 생성되면 절대로 그 값이 변하지 않는다.

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

 

//바뀌는지 확인
String str = "최초 문자열";
System.out.println("str1 = " + str1); // str = 최초 문자열
str = "변경된 문자열";
System.out.println("str1 = " + str1); // str = 변경된 문자열

잘만 바뀌는데...?
 
바뀌는 것처럼 보이는 거다.

str 이라는 String 객체가 생성된 이후 "최초 문자열""변경된 문자열" 로 바꾼다고 해도!

실제 내부적으로는 최초 생성된 String 객체의 값이 변경되는 것이 아니라

새로운 String 객체가 생성되어 그 참조가 str 변수에 할당된 것입니다.

즉,  최초에 생성된 "최초 문자열" 과 "변경된 문자열" 두 개의 인스턴스가 Heap 영역에 저장되어 있는 상태다.
(이후 참조되지 않는 "최초 문자열"은 Gabage Collection을 통해 제거)

 

그럼 왜 String 객체가 불변인걸까?
  • 보안
    String은 다수의 Java 클래스에서 매개 변수로 널리 사용되고 있다.
    예를들어, 네트워크를 연결할 때 호스트 및 포트가 String으로 되어있고, Java에서 파일을 읽어 들이기 위한 파일이나 디렉토리 경로도 String으로 되어있으며, 데이터베이스 연결에 필요한 URL도 역시 String으로 되어있다.

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

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

    String을 final으로 지정함으로써 JVM은 올바른 클래스들을 로드할 수 있게 된다.
  • Hash
    String은 HashMap, Hashtable 같은 해시 기반 컬렉션의 키로써 많이 사용되기 때문에 동일한 값으로 저장된 객체의 value를 검색할 수 있도록 불변한 것이 중요하다.

    키로 지정된 문자열의 내용이 수정된 경우 삽입 및 검색 시 두 개의 다른 해시 코드를 생성하여 잠재적으로 맵에서 값 객체를 잃게 된다

  • 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에 있는 객체를 사용한다. 따라서 객체가 새로 생성되는 동작이 줄어들기 때문에 같은 문자열이 재사용되는 빈도가 높을수록 성능이 향상될 수 있다.

★불변성의 개념 강조!

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

'Back-end > Java' 카테고리의 다른 글

추상(abstract)  (0) 2023.04.30
Generic  (0) 2023.04.29
static  (0) 2023.04.17
JVM(Java Virtual Machine)  (0) 2023.04.17
Method(메소드)  (0) 2023.04.16