为什么他们决定在Java和。net(和其他一些语言)中使字符串不可变?为什么不让它变呢?
当前回答
这主要是出于安全考虑。如果您不能相信您的字符串是防篡改的,那么保护系统就困难得多。
其他回答
在大多数情况下,“字符串”(被用作/视为/认为/假定)是一个有意义的原子单位,就像一个数字一样。
因此,问为什么字符串的单个字符是不可变的,就像问为什么整数的单个比特是不可变的一样。
你应该知道原因。想想看。
我不想这么说,但不幸的是,我们正在讨论这个问题,因为我们的语言很糟糕,我们试图使用一个单一的词,字符串,来描述一个复杂的,上下文定位的概念或对象类。
我们对“字符串”执行计算和比较,类似于对数字的操作。如果字符串(或整数)是可变的,我们必须编写特殊的代码来将它们的值锁定为不可变的局部形式,以便可靠地执行任何类型的计算。因此,最好将字符串视为数字标识符,但它可能是数百位,而不是16位、32位或64位。
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.
考虑一下如果字符串是可变的会是什么样子。如果可变用户名字符串在此函数使用时被另一个线程有意或无意地修改,则以下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 );
}
}
安全不仅仅是“访问控制”,它还涉及“安全性”和“保证正确性”。如果一个方法不容易编写,也不能可靠地依靠它来执行简单的计算或比较,那么调用它是不安全的,但是对编程语言本身提出质疑是安全的。
根据Effective Java,第4章,第73页,第二版:
"There are many good reasons for this: Immutable classes are easier to design, implement, and use than mutable classes. They are less prone to error and are more secure. [...] "Immutable objects are simple. An immutable object can be in exactly one state, the state in which it was created. If you make sure that all constructors establish class invariants, then it is guaranteed that these invariants will remain true for all time, with no effort on your part. [...] Immutable objects are inherently thread-safe; they require no synchronization. They cannot be corrupted by multiple threads accessing them concurrently. This is far and away the easiest approach to achieving thread safety. In fact, no thread can ever observe any effect of another thread on an immutable object. Therefore, immutable objects can be shared freely [...]
同一章的其他要点:
不仅可以共享不可变对象,还可以共享它们的内部结构。 […] 不可变对象为其他对象提供了很好的构建块,无论是可变的还是不可变的。 […] 不可变类的唯一缺点是,它们需要为每个不同的值提供一个单独的对象。
一个因素是,如果字符串是可变的,那么存储字符串的对象必须小心地存储副本,以免它们的内部数据在没有通知的情况下发生变化。鉴于字符串是一种相当基本的类型,就像数字一样,即使它们是通过引用传递的,也可以把它们当作是按值传递的,这是很好的(这也有助于节省内存)。
线程安全和性能。如果一个字符串不能被修改,那么在多个线程之间传递引用是安全且快速的。如果字符串是可变的,则总是必须将字符串的所有字节复制到新实例,或者提供同步。一个典型的应用程序在每次需要修改字符串时将读取字符串100次。参见维基百科关于不变性的内容。
我知道这是个意外,但是… 它们真的是不可变的吗? 考虑以下几点。
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);
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循环和for-each循环在性能上有区别吗?
- 你如何比较两个版本的字符串在Java?
- 为什么在Java和。net中不能修改字符串?
- java.util.Random真的那么随机吗?我怎么能生成52!(阶乘)可能的序列?
- ZoneOffset之间的区别是什么。UTC和ZoneId.of(“UTC”)?
- 类未找到:IntelliJ中的空测试套件
- 将JAR文件添加到Spark作业- Spark -submit
- REST API - dto还是不是?
- JavaFX应用程序图标
- Java:强/软/弱/幻影引用的区别
- 在序列化和反序列化期间JSON属性的不同名称
- 如何创建一个日期对象从字符串在javascript
- 获取Android设备名称
- Gradle代理配置
- 如何获得具有已知资源名称的资源id ?