为什么他们决定在Java和。net(和其他一些语言)中使字符串不可变?为什么不让它变呢?


当前回答

哇!我不敢相信这里的错误信息。不可变的字符串与安全性无关。如果某人已经可以访问正在运行的应用程序中的对象(如果你试图防止某人在你的应用程序中“入侵”字符串,就必须假设这一点),那么他们肯定有很多其他可用的黑客机会。

String的不可变性解决了线程问题,这是一个相当新颖的想法。嗯…我有一个被两个不同线程改变的对象。我如何解决这个问题?同步对对象的访问?Naawww……让我们不要让任何人改变对象——这将解决我们所有混乱的并发问题!事实上,让我们让所有对象都是不可变的,然后我们就可以从Java语言中删除synchronized结构。

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.

在Java中,这被称为实习。这里的Java编译器只是遵循了编译器几十年来所做的标准内存优化。为了解决这些String字面值在运行时被修改的相同问题,Java简单地使String类不可变(即,不提供允许您更改String内容的setter)。如果字符串字面量没有发生转换,字符串就不必是不可变的。

其他回答

根据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 [...]

同一章的其他要点:

不仅可以共享不可变对象,还可以共享它们的内部结构。 […] 不可变对象为其他对象提供了很好的构建块,无论是可变的还是不可变的。 […] 不可变类的唯一缺点是,它们需要为每个不同的值提供一个单独的对象。

哇!我不敢相信这里的错误信息。不可变的字符串与安全性无关。如果某人已经可以访问正在运行的应用程序中的对象(如果你试图防止某人在你的应用程序中“入侵”字符串,就必须假设这一点),那么他们肯定有很多其他可用的黑客机会。

String的不可变性解决了线程问题,这是一个相当新颖的想法。嗯…我有一个被两个不同线程改变的对象。我如何解决这个问题?同步对对象的访问?Naawww……让我们不要让任何人改变对象——这将解决我们所有混乱的并发问题!事实上,让我们让所有对象都是不可变的,然后我们就可以从Java语言中删除synchronized结构。

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.

在Java中,这被称为实习。这里的Java编译器只是遵循了编译器几十年来所做的标准内存优化。为了解决这些String字面值在运行时被修改的相同问题,Java简单地使String类不可变(即,不提供允许您更改String内容的setter)。如果字符串字面量没有发生转换,字符串就不必是不可变的。

几乎每条规则都有例外:

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();
            }
        }
    }
}

人们应该问,“为什么X应该是可变的?”最好默认为不可变,因为Princess Fluff已经提到了它的好处。它应该是一个例外,某些东西是可变的。

不幸的是,目前大多数编程语言都默认为可变性,但希望未来的默认更多地是不变性(参见下一个主流编程语言的愿望清单)。

这主要是出于安全考虑。如果您不能相信您的字符串是防篡改的,那么保护系统就困难得多。