Java 및 .NET에서 문자열을 변경할 수없는 이유는 무엇입니까?


191

StringJava 및 .NET (및 기타 언어)에서 변경 불가능한 이유는 무엇 입니까? 왜 그것들을 변경하지 않았습니까?


13
나는 같은 생각을했지만 원래 포스터 위치를 확인하고 벨기에 출신임을 알았습니다. 이는 영어 원어민이 아닐 가능성이 높다는 것을 의미합니다. 대부분의 원주민이 언어를 느슨하게 이해했다는 사실과 함께, 나는 그녀에게 약간의 여유를 줄이기로 결정했습니다.
belugabob

8
belugabob에게 감사하지만, 나는 그녀가 아니에요. 분명히 사람들은 문화적 차이를 고려하지 않습니다.
chrissie1

7
나의 사과 – chrissie는 (일반적으로) 영국에서 소녀의 이름이다-나에게 또 다른 문화적 차이의 희생자가
되다

참고로 .NET String에서는 실제로 내부적으로 변경 가능합니다. StringBuilder.NET 2.0에서는 문자열을 변경합니다 . 그냥 여기 두겠습니다
Alvin Wong

실제로 .NET 문자열 변경 가능합니다. 그리고 그것은 해킹조차도 아닙니다.
Bitterblue

답변:


205

Effective Java의 4 장 73 페이지 2 판 에 따르면 :

"이에 대한 여러 가지 이유가 있습니다. 불변 클래스는 변경 가능한 클래스보다 설계, 구현 및 사용하기가 더 쉽습니다. 오류가 덜 발생하고 더 안전합니다.

[...]

" 불변 객체는 단순하다. 불변 객체는 생성 된 상태 인 정확히 하나의 상태 일 수있다. 모든 생성자가 클래스 불변을 설정하게한다면, 이러한 불변은 항상 참으로 유지 될 것이다. 노력하지 마십시오.

[...]

불변의 객체는 본질적으로 스레드로부터 안전합니다. 동기화가 필요하지 않습니다. 여러 스레드가 동시에 액세스하여 손상 될 수 없습니다. 이것은 스레드 안전성을 달성하는 가장 쉬운 방법입니다. 실제로 어떤 스레드도 다른 스레드가 불변 개체에 미치는 영향을 관찰 할 수 없습니다. 따라서 불변 개체를 자유롭게 공유 할 수 있습니다

[...]

같은 장의 다른 작은 점들 :

변경 불가능한 객체를 공유 할 수있을뿐만 아니라 내부를 공유 할 수 있습니다.

[...]

불변 객체는 변경 가능하거나 불변의 다른 객체에 대한 훌륭한 빌딩 블록을 만듭니다.

[...]

불변 클래스의 유일한 단점은 각각의 고유 한 값에 대해 별도의 객체가 필요하다는 것입니다.


22
내 대답의 두 번째 문장을 읽으십시오. 불변 클래스는 변경 가능한 클래스보다 설계, 구현 및 사용하기가 더 쉽습니다. 오류가 적고 더 안전합니다.
프린세스 플러 프

5
@PRINCESSFLUFF 나는 단일 스레드에서도 가변 문자열을 공유하는 것이 위험하다고 덧붙였다. 예를 들어, 보고서 복사 : report2.Text = report1.Text;. 그런 다음 다른 곳에서 텍스트를 수정하십시오 report2.Text.Replace(someWord, someOtherWord);. 이것은 첫 번째 보고서와 두 번째 보고서를 변경합니다.
phoog

10
@Sam은 "왜 변경할 수 없는지"묻지 않았으며 "불변성을 결정한 이유"를 물었습니다.
James

1
@PRINCESSFLUFF이 답변은 문자열을 구체적으로 다루지 않습니다. 그것은 OPs 질문이었습니다. 너무 실망 스럽습니다. 이것은 항상 SO와 String 불변성 질문과 함께 발생합니다. 여기에 대한 답변은 불변성의 일반적인 이점에 대해 이야기합니다. 그렇다면 왜 모든 유형이 불변이 아닌가? 돌아가서 String을 말씀해 주시겠습니까?
Howiecamp

@ Howiecamp 나는 문자열이 변경 될 수 있다는 대답에 의해 암시 적이라고 생각합니다 (가설에서 변경 가능한 문자열 클래스가 존재하는 것을 막는 것은 없습니다). 그들은 단순성을 위해 그렇게하지 않기로 결정했으며 99 %의 사용 사례를 다루기 때문입니다. 그들은 여전히 ​​다른 1 % 사례에 대해 StringBuilder를 제공했습니다.
다니엘 가르시아 루비오

102

최소한 두 가지 이유가 있습니다.

첫 번째-보안 http://www.javafaq.nu/java-article1060.html

String이 변경 불가능한 주된 이유는 보안이었습니다. 이 예제를 살펴보십시오. 로그인 확인이 가능한 파일 열기 방법이 있습니다. 호출이 OS로 전달되기 전에 필요한 인증을 처리하기 위해이 메소드에 문자열을 전달합니다. 문자열이 변경 가능하면 OS가 프로그램에서 요청을 받기 전에 인증 확인 후 내용을 수정하는 것이 가능했으며 파일을 요청할 수 있습니다. 따라서 사용자 디렉토리에서 텍스트 파일을 열 수있는 권한이 있지만 파일 이름을 변경하는 경우 즉시 "passwd"파일 또는 다른 파일을 열도록 요청할 수 있습니다. 그런 다음 파일을 수정할 수 있으며 OS에 직접 로그인 할 수 있습니다.

둘째-메모리 효율성 http://hikrish.blogspot.com/2006/07/why-string-class-is-immutable.html

JVM은 내부적으로 "문자열 풀"을 유지 보수합니다. 메모리 효율성을 달성하기 위해 JVM은 풀에서 문자열 오브젝트를 참조합니다. 새 String 객체를 만들지 않습니다. 따라서 새 문자열 리터럴을 작성할 때마다 JVM이 풀이 있는지 여부를 풀에서 체크인합니다. 풀에 이미 존재하는 경우 동일한 오브젝트에 대한 참조를 제공하거나 풀에 새 오브젝트를 작성하십시오. 동일한 String 객체를 가리키는 많은 참조가있을 수 있습니다. 누군가 값을 변경하면 모든 참조에 영향을 미칩니다. 그래서 태양은 그것을 불변으로 만들기로 결정했습니다.


재사용에 대한 좋은 지적이며 String.intern ()을 사용하는 경우 특히 그렇습니다. 모든 문자열을 불변으로 만들지 않고 재사용 할 수 있었지만 그 시점에서 인생은 복잡 해지는 경향이 있습니다.
jsight

3
그 어느 쪽도이 시대에 나에게 타당한 이유가 아닌 것 같습니다.
Brian Knoblauch

1
메모리 효율 인수에 너무 확신하지 못합니다 (즉, 두 개 이상의 String 객체가 동일한 데이터를 공유하고 하나는 수정되면 둘 다 수정됩니다). MFC의 CString 개체는 참조 계산을 사용하여 문제를 해결합니다.
RobH

7
보안은 실제로 불변 문자열에 대한 Raison d' être의 일부가 아닙니다. OS는 타이밍 공격을 피하기 위해 문자열을 커널 모드 버퍼에 복사하고 액세스 검사를 수행합니다. 스레드 안전성 및 성능에 관한 모든 것 :)
snemarch

1
메모리 효율 인수도 작동하지 않습니다. C와 같은 모국어에서 문자열 상수는 초기화 된 데이터 섹션의 데이터에 대한 포인터 일뿐입니다. 어쨌든 읽기 전용 / 불변입니다. "누군가가 값을 변경하면"-다시 풀의 문자열은 읽기 전용입니다.
wj32

57

실제로, 문자열이 Java에서 불변 인 이유는 보안과 관련이 없습니다. 두 가지 주요 이유는 다음과 같습니다.

Thead 안전 :

문자열은 매우 널리 사용되는 객체 유형입니다. 따라서 멀티 스레드 환경에서 사용되는 것이 다소 보장됩니다. 문자열은 스레드간에 문자열을 안전하게 공유 할 수 있도록 변경할 수 없습니다. 변경 불가능한 문자열이 있으면 스레드 A에서 다른 스레드 B로 문자열을 전달할 때 스레드 B가 스레드 A의 문자열을 예기치 않게 수정할 수 없습니다.

이렇게하면 이미 복잡한 멀티 스레드 프로그래밍 작업을 단순화 할 수있을뿐만 아니라 멀티 스레드 응용 프로그램의 성능도 향상시킬 수 있습니다. 하나의 스레드가 다른 스레드에 의해 수정되는 동안 하나의 스레드가 개체의 값을 읽으려고 시도하지 않도록 변경 가능한 개체에 대한 액세스는 여러 스레드에서 액세스 할 수있을 때 동기화되어야합니다. 적절한 동기화는 프로그래머에게 올바르게 수행하기 어렵고 런타임에 비용이 많이 듭니다. 변경할 수없는 객체는 수정할 수 없으므로 동기화 할 필요가 없습니다.

공연:

String interning이 언급되었지만 Java 프로그램의 메모리 효율성이 약간만 향상되었습니다. 문자열 리터럴 만 인터 턴됩니다. 즉, 소스 코드 에서 동일한 문자열 만 동일한 문자열 객체를 공유합니다. 프로그램이 동일한 문자열을 동적으로 작성하면 다른 오브젝트로 표시됩니다.

더 중요한 것은 불변 문자열을 사용하여 내부 데이터를 공유 할 수 있다는 것입니다. 많은 문자열 작업의 경우 기본 문자 배열을 복사 할 필요가 없습니다. 예를 들어, String의 첫 다섯 문자를 사용한다고 가정하십시오. Java에서는 myString.substring (0,5)을 호출합니다. 이 경우 substring () 메서드는 myString의 기본 char []를 공유하지만 인덱스 0에서 시작하여 해당 char []의 인덱스 5에서 끝나는 것을 알고있는 새 String 객체를 만드는 것입니다. 이것을 그래픽 형식으로 넣으려면 다음과 같이 끝납니다.

 |               myString                  |
 v                                         v
"The quick brown fox jumps over the lazy dog"   <-- shared char[]
 ^   ^
 |   |  myString.substring(0,5)

이것은 이런 종류의 연산을 매우 저렴하게 만들고, 연산은 원래 문자열의 길이나 추출해야하는 부분 문자열의 길이에 의존하지 않기 때문에 O (1)입니다. 많은 문자열이 기본 char []를 공유 할 수 있기 때문에이 동작에는 메모리 이점도 있습니다.


6
기본을 공유하는 참조로 하위 문자열을 구현 char[]하는 것은 다소 의심스러운 디자인 결정입니다. 전체 파일을 단일 문자열로 읽고 1 문자 하위 문자열에 대한 참조를 유지하는 경우 전체 파일을 메모리에 보관해야합니다.
Gabe

5
정확히, 나는 전체 페이지에서 몇 단어를 추출하는 데 필요한 웹 사이트 크롤러를 만드는 동안 특정 문제에 부딪 쳤습니다. 전체 페이지 HTML 코드는 메모리에 있었고 char []을 공유하는 하위 문자열로 인해 몇 바이트 만 필요하더라도 전체 HTML 코드를 유지했습니다. 이에 대한 해결 방법은 new String (original.substring (.., ..))을 사용하는 것입니다. String (String) 생성자는 기본 배열의 관련 범위 사본을 만듭니다.
LordOfThePigs

1
후속 변경 사항을 다루는 부록 : Jave 7부터 String.substring()위의 주석에서 언급 된 문제를 방지하기 위해 전체 사본을 수행합니다. 자바 8에서 두 가능 필드 char[]즉, 공유 count하고는 offset, 따라서 문자열 인스턴스의 메모리 풋 프린트를 감소, 제거됩니다.
Christian Semrau

Thead Safety 부분에 동의하지만 하위 문자열 사례를 의심합니다.
Gqqnbig

@LoveRight : 그런 다음 java.lang.String ( grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/… ) 의 소스 코드를 확인하십시오 .Java 6까지는 이 답변이 작성되었을 때 최신 상태였습니다). Java 7에서 분명히 변경되었습니다.
LordOfThePigs

28

나사산 안전 및 성능. 문자열을 수정할 수없는 경우 여러 스레드간에 참조를 전달하는 것이 안전하고 빠릅니다. 문자열이 변경 가능한 경우 항상 문자열의 모든 바이트를 새 인스턴스에 복사하거나 동기화를 제공해야합니다. 일반적인 응용 프로그램은 문자열을 수정해야 할 때마다 문자열을 100 번 읽습니다. 불변성 에 대한 위키 백과를 참조하십시오 .



7

와! 나는 잘못된 정보를 믿을 수 없다. String불변의 것은 보안에 아무런 영향을 미치지 않습니다. 누군가가 이미 실행중인 응용 프로그램의 객체에 액세스 할 수 있다면 ( String앱에서 누군가 '해킹'을 막으려 고한다고 가정 해야하는 경우 ) 분명히 해킹에 사용할 수있는 다른 많은 기회가 될 것입니다.

불변성이 String스레딩 문제를 해결 한다는 것은 매우 참신한 생각입니다 . 흠 ... 두 개의 다른 스레드로 변경되는 객체가 있습니다. 이 문제를 어떻게 해결합니까? 객체에 대한 액세스를 동기화 하시겠습니까? Naawww ... 아무도 객체를 변경하지 못하게합시다. 그러면 우리의 지저분한 동시성 문제가 모두 해결 될 것입니다! 실제로 모든 객체를 변경할 수 없게 만든 다음 Java 언어에서 동기화 된 구성을 제거 할 수 있습니다.

(위의 다른 사람들이 지적한) 진짜 이유는 메모리 최적화입니다. 동일한 응용 프로그램에서 동일한 문자열 리터럴을 반복적으로 사용하는 것이 일반적입니다. 실제로 수십 년 전에 많은 컴파일러가 String리터럴 의 단일 인스턴스 만 저장하도록 최적화했습니다 . 이 최적화의 단점은 String리터럴 을 수정하는 런타임 코드가 이를 공유하는 다른 모든 코드의 인스턴스를 수정하기 때문에 문제를 유발한다는 것입니다. 예를 들어, 응용 프로그램 어딘가의 함수가 String리터럴 "dog"을 로 변경하는 것은 좋지 않습니다 "cat". A는 printf("dog")초래 "cat"표준 출력에 기록된다. 따라서 변경을 시도하는 코드로부터 보호하는 방법이 필요했습니다.String리터럴 (즉, 불변으로 만듭니다). OS에서 지원하는 일부 컴파일러는 String리터럴을 특수 읽기 전용 메모리 세그먼트 에 배치 하여 쓰기 시도가있을 경우 메모리 오류를 일으킬 수 있습니다.

자바에서는 이것을 인턴이라고합니다. 여기서 Java 컴파일러는 수십 년 동안 컴파일러가 수행 한 표준 메모리 최적화를 따르고 있습니다. 그리고 String런타임에 수정되는 이러한 리터럴 과 동일한 문제를 해결하기 위해 Java는 단순히 String클래스를 변경할 수 없게 만듭니다 (즉, String내용 을 변경할 수있는 setter를 제공하지 않음 ). 리터럴 String인터 리닝이 String발생하지 않으면 s를 변경할 수 없습니다 .


3
나는 불변성과 스레딩 주석에 대해 강력하게 동의하지 않습니다. 자바 구현 자 중 한 사람인 조쉬 블로흐 (Josh Bloch)가 이것이 디자인 문제 중 하나라고 말하면 어떻게 잘못된 정보가 될 수 있을까요?
javashlook

1
동기화는 비싸다. 변경 불가능한 오브젝트에 대한 참조는 변경 불가능하므로 동기화되지 않아야합니다. 그것이 모든 객체가 변경 가능하지 않으면 불변으로 만드는 이유입니다. 문자열은 변경할 수 없으므로 여러 스레드에서 더 효율적입니다.
David Thornley

5
@Jim : 메모리 최적화는 'THE'이유가 아니라 'A'이유입니다. 수정 불가능한 객체는 본질적으로 스레드로부터 안전하고 David가 언급 한 것처럼 값 비싼 동기화가 필요하지 않기 때문에 스레드 안전성 또한 'A'이유입니다. 스레드 안전성은 실제로 불변 인 객체의 부작용입니다. 동기화를 객체를 "일시적으로"변경할 수 없게 만드는 방법으로 생각할 수 있습니다 (ReaderWriterLock은 읽기 전용으로 만들고 일반 잠금을 사용하면 액세스 할 수 없게됩니다 (물론 불변도 만듭니다)).
Triynko

1
@DavidThornley : 가변 값 홀더에 대한 여러 개의 독립적 인 참조 경로를 만들면 효과적으로 값을 엔티티로 변환 할 수 있으며 스레딩 문제 외에는 추론하기가 훨씬 어렵습니다. 일반적으로, 가변 객체는 정확히 하나의 참조 경로가 각각 존재하는 경우 불변 객체보다 더 효율적이지만, 불변 객체는 참조를 공유함으로써 객체의 내용을 효율적으로 공유 할 수 있습니다. 가장 좋은 패턴은 Stringand StringBuffer로 표시되지만 불행히도 그 모델을 따르는 다른 유형은 거의 없습니다.
supercat

7

String 는 기본 유형이 아니지만 일반적으로 값 의미와 함께 값을 사용하려고합니다 (예 : 값).

가치는 당신이 등 뒤에서 변하지 않을 것이라고 믿을 수있는 것입니다. 당신이 쓰는 경우 : String str = someExpr(); 당신은 무언가를하지 않는 한 변경을 원하지 않습니다 str.

String과 같이 Object자연스럽게 포인터 의미를 가지고 잘 불변 할 필요가 같은 값의 의미를 얻을 수 있습니다.


7

한 가지 요인은 Strings가 변경 가능하면 String내부 데이터가 예고없이 변경되지 않도록 사본을 저장 하는 객체 를주의해서 사용해야한다는 것입니다. 점을 감안 String의 숫자와 같은 매우 원시적 형, 그들이 값에 의해 전달 된 것처럼 하나는 그들이 (또한 메모리에 저장하는 데 도움) 참조에 의해 전달되는 경우에도, 그들을 치료 할 수있을 때 좋다.


6

나는 이것이 범프라는 것을 알고 있지만 ... 그들은 정말로 불변인가? 다음을 고려하세요.

public static unsafe void MutableReplaceIndex(string s, char c, int i)
{
    fixed (char* ptr = s)
    {
        *((char*)(ptr + i)) = c;
    }
}

...

string s = "abc";
MutableReplaceIndex(s, '1', 0);
MutableReplaceIndex(s, '2', 1);
MutableReplaceIndex(s, '3', 2);
Console.WriteLine(s); // Prints 1 2 3

확장 방법으로 만들 수도 있습니다.

public static class Extensions
{
    public static unsafe void MutableReplaceIndex(this string s, char c, int i)
    {
        fixed (char* ptr = s)
        {
            *((char*)(ptr + i)) = c;
        }
    }
}

다음 작업을 수행합니다.

s.MutableReplaceIndex('1', 0);
s.MutableReplaceIndex('2', 1);
s.MutableReplaceIndex('3', 2);

결론 : 컴파일러에서 알려진 불변 상태에 있습니다. Java에는 포인터가 없으므로 위의 .NET 문자열에만 적용됩니다. 그러나 C #에서 포인터를 사용하여 문자열을 완전히 변경할 수 있습니다. 포인터의 사용 방법, 실제 사용 방법 또는 안전하게 사용되는 방법이 아닙니다. 그러나 가능하므로 전체 "변경 가능"규칙이 적용되지 않습니다. 일반적으로 문자열의 색인을 직접 수정할 수 없으며 이것이 유일한 방법입니다. 문자열의 포인터 인스턴스를 허용하지 않거나 문자열을 가리킬 때 복사를 수행하여이를 방지 할 수있는 방법이 있습니다.


1
+1. .NET 문자열은 실제로 변경할 수 없습니다. 실제로 이것은 성능상의 이유로 String 및 StringBuilder 클래스에서 항상 수행됩니다.
James Ko

3

대부분의 경우, "문자열"은 숫자와 마찬가지로 의미있는 원자 단위 (사용 / 처리 된 것으로 간주 / 처리 된 것으로 가정) 입니다.

따라서 문자열의 개별 문자를 변경할 수없는 이유를 묻는 것은 정수의 개별 비트를 변경할 수없는 이유를 묻는 것과 같습니다.

이유를 알아야합니다. 생각 해봐.

나는 그것을 말하기는 싫지만, 불행히도 우리는 언어가 빨라서 복잡하고 상황에 맞는 개념이나 객체 클래스를 설명하기 위해 단일 단어 string 을 사용하려고하기 때문에 이것을 토론하고 있습니다 .

우리는 숫자로하는 것과 비슷한 "문자열"로 계산하고 비교합니다. 문자열 (또는 정수)이 변경 가능하면 모든 종류의 계산을 안정적으로 수행하기 위해 값을 변경할 수없는 로컬 형식으로 고정하는 특수 코드를 작성해야합니다. 따라서 문자열을 숫자 식별자처럼 생각하는 것이 가장 좋지만 16, 32 또는 64 비트 길이 대신 수백 비트 길이 일 수 있습니다.

누군가가 "현"이라고 말할 때, 우리는 모두 다른 것을 생각합니다. 특정 목적을 염두에 두지 않고 단순히 문자 집합으로 생각하는 사람들은 누군가가 그 문자를 조작 할 수 없어야한다고 결정한 것에 당혹 스럽습니다 . 그러나 "string"클래스는 단순한 문자 배열이 아닙니다. 이 STRING아니라 char[]. 우리가 "문자열"이라고하는 개념에 대한 몇 가지 기본 가정이 있으며 일반적으로 숫자와 같이 의미있는 원자 단위의 코딩 된 데이터로 설명 할 수 있습니다. 사람들이 "문자열 조작"에 대해 이야기 할 때, 실제로 문자열 을 작성하기 위해 문자 를 조작하는 것에 대해 이야기하고있을 것 입니다.

문자열이 변경 가능한 경우 어떻게 될지 잠시 생각해보십시오. 이 함수를 사용하는 동안 다른 사용자가 변경 가능한 사용자 이름 문자열을 의도적으로 또는 실수로 수정 ​​한 경우 다음 API 함수를 사용하여 다른 사용자에 대한 정보를 반환 할 수 있습니다 .

string GetPersonalInfo( string username, string password )
{
    string stored_password = DBQuery.GetPasswordFor( username );
    if (password == stored_password)
    {
        //another thread modifies the mutable 'username' string
        return DBQuery.GetPersonalInfoFor( username );
    }
}

보안은 '액세스 제어'뿐만 아니라 '안전성'및 '정확성 보장'에 관한 것입니다. 간단한 계산이나 비교를 안정적으로 수행하기 위해 메소드를 쉽게 작성할 수없고 의존 할 수 있다면 호출하는 것이 안전하지 않지만 프로그래밍 언어 자체에 의문을 제기하는 것이 안전합니다.


C #에서 문자열은 포인터 (use unsafe) 또는 단순히 리플렉션 (기본 필드를 쉽게 얻을 수 있음)을 통해 변경할 수 있습니다. 이것은 의도적 으로 문자열을 변경하려는 사람이 쉽게 할 수 있기 때문에 보안에 대한 요점을 무효화 합니다. 그러나 프로그래머에게 보안을 제공합니다. 특별한 작업을 수행하지 않으면 문자열이 변경되지 않지만 스레드 안전성은 보장되지 않습니다!
Abel

예, 포인터를 통해 모든 데이터 객체 (문자열, 정수 등)의 바이트를 변경할 수 있습니다. 그러나 문자열 클래스가 문자 수정을 위해 공개 메소드가 내장되어 있지 않다는 점에서 왜 문자열 클래스를 변경할 수 없는지에 대해 이야기하고 있습니다. 나는 개별 문자를 조작하는 것이 숫자의 개별 비트를 조작하는 것 (문자열을 바이트 배열이 아닌 전체 토큰으로 취급 할 때)과 숫자를 비트 값이 아닌 숫자 값 우리는 하위 오브젝트 레벨이 아닌 개념적 오브젝트 레벨에서 이야기하고 있습니다
Triynko

2
그리고 명확히하기 위해 객체 지향 코드의 포인터는 클래스에 대해 정의 된 공용 인터페이스를 우회하기 때문에 본질적으로 안전하지 않습니다. 내가 말한 것은 문자열에 대한 공용 인터페이스가 다른 스레드에 의해 수정되도록 허용하면 함수를 쉽게 속일 수 있다는 것입니다. 물론 포인터를 사용하여 직접 데이터에 액세스하면 쉽게 속일 수 있지만 쉽게 또는 실수로 할 수는 없습니다.
Triynko

1
'객체 지향 코드의 포인터는 참조 라고 부르지 않는 한 본질적으로 안전하지 않습니다 . Java의 참조는 C ++의 포인터와 다르지 않습니다 (포인터 산술 만 비활성화 됨). 다른 개념은 관리하거나 수동으로 할 수있는 메모리 관리이지만 다른 것입니다. 당신은 GC가 (반대 도달의 의미가 더 열심히 청소하지만, unfeasable하지 만들 수있을 것이라는 의미에서 더 어려울 것)하지 않고 참조 시멘틱스 (아무 산술 포인터)를 가질 수있다
dribeas - 데이비드 로드리게스

다른 것은 문자열이 거의 변경 불가능하지만 그렇지 않은 경우 (여기서 CLI를 충분히 알지 못함) 보안상의 이유로 실제로 나쁠 수 있다는 것입니다. 일부 오래된 Java 구현에서는 그렇게 할 수 있으며 문자열 을 내부화 하는 데 사용되는 코드 스 니펫을 발견했습니다 (같은 값을 가진 다른 내부 문자열을 찾고 포인터를 공유하고 이전 메모리 블록을 제거하십시오). 다른 클래스에서 잘못된 동작을 강제하는 문자열 내용을 다시 작성하십시오. (고려는 "SELECT *"다시 쓰기 "DELETE"로)
데이비드 로드리게스 - dribeas

3

불변성은 보안과 밀접한 관련이 없습니다. 이를 위해 적어도 .NET에서는 SecureString클래스 를 얻습니다 .

나중에 편집 : Java에서는 GuardedString비슷한 구현이 있습니다.


2

C ++에서 문자열 변경 가능 여부를 결정하면 많은 문제가 발생합니다 . Mad COW Disease 에 대한 Kelvin Henney의이 훌륭한 기사를 참조하십시오 .

COW = 쓰기시 복사.


2

트레이드 오프입니다. 풀로 String이동 String하여 여러 개의 동일한 파일을 만들면 String동일한 메모리를 공유합니다. 설계자들은이 메모리 절약 기술이 일반적인 경우에 잘 작동한다고 생각했습니다.

단점은 연결 String은 단지 과도기적이며 가비지가되어 실제로 메모리 성능을 손상 시키는 많은 추가 기능을 생성 한다는 것입니다. 당신은이 StringBufferStringBuilder(자바, StringBuilder이러한 경우 메모리를 유지하기 위해 사용하는 .NET도있다).


1
"inter ()"로 지정된 문자열을 명시 적으로 사용하지 않으면 "문자열 풀"이 모든 문자열에 자동으로 사용되지는 않습니다 .
jsight

2

StringJava에서는 불변성이 아니며, 리플렉션 및 클래스 로딩을 사용하여 값을 변경할 수 있습니다. 보안을 위해 해당 특성에 의존해서는 안됩니다. 예를 들어 다음을 참조하십시오. Java의 마술 트릭


1
코드가 완전 신뢰로 실행되는 경우에만 그러한 트릭을 수행 할 수 있다고 생각하므로 보안 손실이 없습니다. JNI를 사용하여 문자열이 저장된 메모리 위치에 직접 쓸 수도 있습니다.
Antoine Aubry 2016 년

사실 나는 당신이 반성에 의해 불변의 물체를 바꿀 수 있다고 생각합니다.
Gqqnbig

0

불변성이 좋습니다. 효과적인 Java를 참조하십시오. 문자열을 전달할 때마다 문자열을 복사해야한다면 오류가 발생하기 쉬운 코드가 될 것입니다. 또한 어떤 수정이 어떤 참조에 영향을 미치는지 혼동됩니다. Integer가 int처럼 동작하도록 변경할 수없는 것과 같은 방식으로, String은 프리미티브처럼 동작하도록 변경할 수없는 것으로 동작해야합니다. C ++에서 문자열을 값으로 전달하면 소스 코드에서 명시 적으로 언급하지 않고도이 작업을 수행합니다.


0

거의 모든 규칙에는 예외가 있습니다.

using System;
using System.Runtime.InteropServices;

namespace Guess
{
    class Program
    {
        static void Main(string[] args)
        {
            const string str = "ABC";

            Console.WriteLine(str);
            Console.WriteLine(str.GetHashCode());

            var handle = GCHandle.Alloc(str, GCHandleType.Pinned);

            try
            {
                Marshal.WriteInt16(handle.AddrOfPinnedObject(), 4, 'Z');

                Console.WriteLine(str);
                Console.WriteLine(str.GetHashCode());
            }
            finally
            {
                handle.Free();
            }
        }
    }
}

-1

주로 보안상의 이유입니다. String변조 방지 기능을 믿을 수 없으면 시스템을 보호하는 것이 훨씬 어렵습니다 .


1
"탬 퍼프 루프 (tamperproof)"의 의미에 대한 예를들 수 있습니까? 이 답변은 실제로 문맥에서 벗어난 느낌입니다.
Gergely Orosz
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.