Java 및 .NET에서 문자열을 변경할 수없는 이유는 무엇입니까?
String
Java 및 .NET (및 기타 언어)에서 변경 불가능한 이유는 무엇 입니까? 왜 그것들을 변경하지 않았습니까?
Effective Java의 4 장 73 페이지 2 판 에 따르면 :
"이에 대한 여러 가지 이유가 있습니다. 불변 클래스는 변경 가능한 클래스보다 설계, 구현 및 사용하기가 더 쉽습니다. 오류가 덜 발생하고 더 안전합니다.
[...]
" 불변 객체는 단순하다. 불변 객체는 생성 된 상태 인 정확히 하나의 상태 일 수있다. 모든 생성자가 클래스 불변을 설정하게한다면, 이러한 불변은 항상 참으로 유지 될 것이다. 노력하지 마십시오.
[...]
불변의 객체는 본질적으로 스레드로부터 안전합니다. 동기화가 필요하지 않습니다. 여러 스레드가 동시에 액세스하여 손상 될 수 없습니다. 이것은 스레드 안전성을 달성하는 가장 쉬운 방법입니다. 실제로 어떤 스레드도 다른 스레드가 불변 개체에 미치는 영향을 관찰 할 수 없습니다. 따라서 불변 개체를 자유롭게 공유 할 수 있습니다
[...]
같은 장의 다른 작은 점들 :
변경 불가능한 객체를 공유 할 수있을뿐만 아니라 내부를 공유 할 수 있습니다.
[...]
불변 객체는 변경 가능하거나 불변의 다른 객체에 대한 훌륭한 빌딩 블록을 만듭니다.
[...]
불변 클래스의 유일한 단점은 각각의 고유 한 값에 대해 별도의 객체가 필요하다는 것입니다.
최소한 두 가지 이유가 있습니다.
첫 번째-보안 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 객체를 가리키는 많은 참조가있을 수 있습니다. 누군가 값을 변경하면 모든 참조에 영향을 미칩니다. 그래서 태양은 그것을 불변으로 만들기로 결정했습니다.
실제로, 문자열이 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 []를 공유 할 수 있기 때문에이 동작에는 메모리 이점도 있습니다.
나사산 안전 및 성능. 문자열을 수정할 수없는 경우 여러 스레드간에 참조를 전달하는 것이 안전하고 빠릅니다. 문자열이 변경 가능한 경우 항상 문자열의 모든 바이트를 새 인스턴스에 복사하거나 동기화를 제공해야합니다. 일반적인 응용 프로그램은 문자열을 수정해야 할 때마다 문자열을 100 번 읽습니다. 불변성 에 대한 위키 백과를 참조하십시오 .
"왜 X가 변해야합니까?" Princess Fluff가 이미 언급 한 이점 때문에 불변성을 기본값으로하는 것이 좋습니다 . 변경 가능한 것은 예외입니다.
불행히도 현재의 대부분의 프로그래밍 언어는 변경 가능성이 있지만, 앞으로는 기본값이 불변성에 가깝습니다 ( 다음 주류 프로그래밍 언어에 대한 희망 목록 참조 ).
Wow! I Can't believe the misinformation here. String
s being immutable have nothing with security. If someone already has access to the objects in a running application (which would have to be assumed if you are trying to guard against someone 'hacking' a String
in your app), they would certainly be a plenty of other opportunities available for hacking.
It's a quite novel idea that the immutability of String
is addressing threading issues. Hmmm ... I have an object that is being changed by two different threads. How do I resolve this? synchronize access to the object? Naawww ... let's not let anyone change the object at all -- that'll fix all of our messy concurrency issues! In fact, let's make all objects immutable, and then we can removed the synchonized contruct from the Java language.
The real reason (pointed out by others above) is memory optimization. It is quite common in any application for the same string literal to be used repeatedly. It is so common, in fact, that decades ago, many compilers made the optimization of storing only a single instance of a String
literal. The drawback of this optimization is that runtime code that modifies a String
literal introduces a problem because it is modifying the instance for all other code that shares it. For example, it would be not good for a function somewhere in an application to change the String
literal "dog"
to "cat"
. A printf("dog")
would result in "cat"
being written to stdout. For that reason, there needed to be a way of guarding against code that attempts to change String
literals (i. e., make them immutable). Some compilers (with support from the OS) would accomplish this by placing String
literal into a special readonly memory segment that would cause a memory fault if a write attempt was made.
In Java this is known as interning. The Java compiler here is just following an standard memory optimization done by compilers for decades. And to address the same issue of these String
literals being modified at runtime, Java simply makes the String
class immutable (i. e, gives you no setters that would allow you to change the String
content). String
s would not have to be immutable if interning of String
literals did not occur.
String
is not a primitive type, yet you normally want to use it with value semantics, i.e. like a value.
A value is something you can trust won't change behind your back. If you write: String str = someExpr();
You don't want it to change unless YOU do something with str
.
String
as an Object
has naturally pointer semantics, to get value semantics as well it needs to be immutable.
One factor is that, if String
s were mutable, objects storing String
s would have to be careful to store copies, lest their internal data change without notice. Given that String
s are a fairly primitive type like numbers, it is nice when one can treat them as if they were passed by value, even if they are passed by reference (which also helps to save on memory).
I know this is a bump, but... Are they really immutable? Consider the following.
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
You could even make it an extension method.
public static class Extensions
{
public static unsafe void MutableReplaceIndex(this string s, char c, int i)
{
fixed (char* ptr = s)
{
*((char*)(ptr + i)) = c;
}
}
}
Which makes the following work
s.MutableReplaceIndex('1', 0);
s.MutableReplaceIndex('2', 1);
s.MutableReplaceIndex('3', 2);
Conclusion: They're in an immutable state which is known by the compiler. Of couse the above only applies to .NET strings as Java doesn't have pointers. However a string can be entirely mutable using pointers in C#. It's not how pointers are intended to be used, has practical usage or is safely used; it's however possible, thus bending the whole "mutable" rule. You can normally not modify an index directly of a string and this is the only way. There is a way that this could be prevented by disallowing pointer instances of strings or making a copy when a string is pointed to, but neither is done, which makes strings in C# not entirely immutable.
For most purposes, a "string" is (used/treated as/thought of/assumed to be) a meaningful atomic unit, just like a number.
Asking why the individual characters of a string are not mutable is therefore like asking why the individual bits of an integer are not mutable.
You should know why. Just think about it.
I hate to say it, but unfortunately we're debating this because our language sucks, and we're trying to using a single word, string, to describe a complex, contextually situated concept or class of object.
We perform calculations and comparisons with "strings" similar to how we do with numbers. If strings (or integers) were mutable, we'd have to write special code to lock their values into immutable local forms in order to perform any kind of calculation reliably. Therefore, it is best to think of a string like a numeric identifier, but instead of being 16, 32, or 64 bits long, it could be hundreds of bits long.
When someone says "string", we all think of different things. Those who think of it simply as a set of characters, with no particular purpose in mind, will of course be appalled that someone just decided that they should not be able to manipulate those characters. But the "string" class isn't just an array of characters. It's a STRING
, not a char[]
. There are some basic assumptions about the concept we refer to as a "string", and it generally can be described as meaningful, atomic unit of coded data like a number. When people talk about "manipulating strings", perhaps they're really talking about manipulating characters to build strings, and a StringBuilder is great for that. Just think a bit about what the word "string" truly means.
Consider for a moment what it would be like if strings were mutable. The following API function could be tricked into returning information for a different user if the mutable username string is intentionally or unintentionally modified by another thread while this function is using it:
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 );
}
}
Security isn't just about 'access control', it's also about 'safety' and 'guaranteeing correctness'. If a method can't be easily written and depended upon to perform a simple calculation or comparison reliably, then it's not safe to call it, but it would be safe to call into question the programming language itself.
Immutability is not so closely tied to security. For that, at least in .NET, you get the SecureString
class.
Later edit: In Java you will find GuardedString
, a similar implementation.
The decision to have string mutable in C++ causes a lot of problems, see this excellent article by Kelvin Henney about Mad COW Disease.
COW = Copy On Write.
It's a trade off. String
s go into the String
pool and when you create multiple identical String
s they share the same memory. The designers figured this memory saving technique would work well for the common case, since programs tend to grind over the same strings a lot.
The downside is that concatenations make a lot of extra String
s that are only transitional and just become garbage, actually harming memory performance. You have StringBuffer
and StringBuilder
(in Java, StringBuilder
is also in .NET) to use to preserve memory in these cases.
String
s in Java are not truly immutable, you can change their value's using reflection and or class loading. You should not be depending on that property for security. For examples see: Magic Trick In Java
Immutability is good. See Effective Java. If you had to copy a String every time you passed it around, then that would be a lot of error-prone code. You also have confusion as to which modifications affect which references. In the same way that Integer has to be immutable to behave like int, Strings have to behave as immutable to act like primitives. In C++ passing strings by value does this without explicit mention in the source code.
There is an exception for nearly almost every rule:
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();
}
}
}
}
It's largely for security reasons. It's much harder to secure a system if you can't trust that your String
s are tamperproof.
참고URL : https://stackoverflow.com/questions/93091/why-cant-strings-be-mutable-in-java-and-net
'Programming' 카테고리의 다른 글
플라스크의 URL로 리디렉션 (0) | 2020.05.14 |
---|---|
수업 연장시 팽창 오류 (0) | 2020.05.14 |
파이썬에서 함수에서 두 값을 어떻게 반환 할 수 있습니까? (0) | 2020.05.14 |
jQuery를 사용하여 텍스트를 클라이언트의 클립 보드에 복사하는 방법은 무엇입니까? (0) | 2020.05.13 |
ES6에서 가져온 객체를 내보내는 방법은 무엇입니까? (0) | 2020.05.13 |