String은 왜 불변일까?

JP·2022년 2월 20일
0

자바

목록 보기
6/10

자바에서 스트링은 불변(Immutable)이다.

자바를 만들때 String을 불변으로 만든 이유로는 크게 3가지가 있다.
1. 메모리 절약
2. 보안
3. 쓰레드 세이프

메모리 절약

public class App {
    public static void main(String[] args) throws Exception {
        String name = "John";
        name = "JP";
        System.out.println(name);//JP 출력
    }
}

위의 코드를 보면 처음에 name을 "John" 으로 선언하였고 그 다음 라인에 "JP"라고 선언을 하였다.
그리고 name의 값은 "JP"로 변경되었다.

이렇게 변하는 값을 왜 불변이라고 하는 것일까?

이것을 알려면 String을 선언할 때 무슨 일이 일어나는지를 파악하는게 중요하다.

String name = "John";

이라고 문자열 변수를 생성하지만 name 변수는 문자열 객체가 아니라, "John" 의 값으로 생성한 메모리에 존재하는 문자열 객체에 대한 참조 값을 의미한다.

그래서 우리는 "John"을 "JP"로 바꾸는 순간, 메모리에 있는 "John"이라는 값 자체를 변경하는 것이 아닌
"JP"라는 값을 새로 만들고 그 값이 있는 메모리의 주소를 참조하는 것이다.

그래서 우리가 String은 Immutable(불변) 하다고 얘기할 때 이 불변의 값은 메모리에 존재하는 String 객체를 말하는 것이다!

이는 자바라는 언어가 스트링을 사용할 때 다음과 같이 메모리를 절약하기 위한 방법으로 만들어졌기 때문이다.

public class App {
    public static void main(String[] args) throws Exception {
        String name = "John";
        String yourName = "John";
        System.out.println(name);
        System.out.println(yourName);
    }
}

자바에서 String이 생성 될 때 자바는 String들을 String Pool 이라는 곳에 넣어서 관리하게 되는데
매번 다른 문자열들이 생성될 때 우선적으로 pool안에 이미 같은 문자열이 있는지 확인한다.

위의 코드에서 name과 yourName값은 같기 때문에 String Pool에서 같은 메모리 주소에 존재하는 "John"을 참조하게 되는 것이다.

만약 우리가 String 을 선언 할 때 단순히 String name = "John"; 으로 선언하지 않고
new를 통해 선언한다면 어떻게 될까?

public class App {
    public static void main(String[] args) throws Exception {
        String name = "John";
        String yourName = "John";
        String otherName = new String("John");
        String newName = new String("John");

        System.out.println(name == yourName); //true
        System.out.println(name == otherName); //false
        System.out.println(otherName == newName); //false
    }
}

결과를 보면 같은 "John" 이더라도 false가 나오게 된다.
왜냐하면 새로운 객체를 만들고 이를 Heap에 넣어 관리한다. 그래서 String Pool과 다른 메모리에 존재하게 되고 다른 참조값을 가지게 되기때문에 false가 나오는 것이다.

보안상의 이유

자바가 이렇게 문자열을 불변하게 만든 이유에는 보안상의 이유도 있다

public class App {
    public static void main(String[] args) throws Exception {
        String name = "John";
        String yourName = new String("John");

        addMoneyToAccount(name,10000);
        addMoneyToAccount(yourName,10000);
    }

    public static void addMoneyToAccount(String account, int money) {
        //some code..
    }

}

우리가 계좌에 돈을 넣는 메서드를 만든다고 가정할 때, "John" 이라는 이름에 10000원을 넣고 싶다.
하지만 알다시피 String name = "John"String yourName = new String("John")은 서로 다른 "John"이다.

또 다른 문제로 메모리상의 값이 변할 수 있게 된다면 다음과 같은 문제를 야기할 수 있다.

public class App {
    public static void main(String[] args) throws Exception {
        String name = "John";
        name = "JP";

        addMoneyToAccount(name,10000);
    }

    public static void addMoneyToAccount(String account, int money) {
        //some code..
    }

}

만약에 name이 가지고 있는 메모리의 해당 문자열 개체를 변경할 수 있도록 허용했다면 addMoneyToAccount 메서드에서
돈을 추가하기 위해 처음에 선언했던 "John"이 아닌 "JP"의 계좌에 돈을 넣게 된다.

하지만 자바에서는 문자열이 변경 불가능하기 때문에 name 변수가 메모리에 동일한 객체를 참조하는지 안하는지에 대한 고민을 덜 수 있고
String Pool에 존재하는 문자열 값들은 절대 변하지 않기 때문에 John이 JP로 바뀌면 어떡하지 등에 대한 걱정을 덜 수 있는 것이다.

쓰레드 세이프

만약 수십, 수백, 수천개의 쓰레드가 name = "John"을 읽는다 해도 이는 String Pool에 있는 단 하나의 "John"을 참조하는 것이고 이 값을 변경할 수 없기때문에 쓰레드 세이프하다 할 수 있다.


참고 영상: Java Strings are Immutable - Here's What That Actually Means

profile
to Infinity and b

0개의 댓글