如果一个字符串是不可变的,这是否意味着......(让我们假设 JavaScript)
var str = 'foo';
alert(str.substr(1)); // oo
alert(str); // foo
这是否意味着,在字符串上调用方法时,它会返回修改后的字符串,但不会更改初始字符串?
如果字符串是可变的,这是否意味着第二个alert()
也会返回oo
?
如果一个字符串是不可变的,这是否意味着......(让我们假设 JavaScript)
var str = 'foo';
alert(str.substr(1)); // oo
alert(str); // foo
这是否意味着,在字符串上调用方法时,它会返回修改后的字符串,但不会更改初始字符串?
如果字符串是可变的,这是否意味着第二个alert()
也会返回oo
?
这意味着一旦你实例化了对象,你就不能改变它的属性。在您的第一个警报中,您没有更改 foo。您正在创建一个新字符串。这就是为什么在您的第二个警报中它会显示“foo”而不是 oo。
这是否意味着,在字符串上调用方法时,它会返回修改后的字符串,但不会更改初始字符串?
是的。字符串一旦创建就无法更改。现在这并不意味着您不能为str
变量分配一个新的字符串对象。您只是无法更改 str 引用的当前对象。
如果字符串是可变的,这是否意味着第二个 alert() 也会返回 oo ?
从技术上讲,不会,因为 substring 方法返回一个新字符串。使对象可变,不会改变方法。使其可变意味着从技术上讲,您可以使子字符串更改原始字符串而不是创建新字符串。
在较低级别上,不变性意味着存储字符串的内存不会被修改。创建字符串后"foo"
,会分配一些内存来存储该值"foo"
。这个记忆不会被改变。如果您修改字符串,例如,substr(1)
将创建一个新字符串并分配内存的不同部分,该部分将存储"oo"
. 现在您在内存中有两个字符串,"foo"
并且"oo"
. 即使你"foo"
不再使用它,它也会一直存在,直到它被垃圾收集。
字符串操作相对昂贵的原因之一。
不可变意味着不能改变或修改。
因此,当您为字符串赋值时,该值是从头开始创建的,而不是被替换。因此,每次将新值分配给同一个字符串时,都会创建一个副本。所以实际上,你永远不会改变原始值。
我不确定 JavaScript,但在 Java 中,字符串通过“字符串常量池”向不变性迈出了额外的一步。字符串可以用字符串文字 ( "foo"
) 或String
类构造函数构造。使用字符串字面量构造的字符串是字符串常量池的一部分,并且相同的字符串字面量将始终来自池中的相同内存地址。
例子:
String lit1 = "foo";
String lit2 = "foo";
String cons = new String("foo");
System.out.println(lit1 == lit2); // true
System.out.println(lit1 == cons); // false
System.out.println(lit1.equals(cons)); // true
在上面,lit1
和lit2
都是使用相同的字符串字面量构造的,因此它们指向相同的内存地址;lit1 == lit2
结果为true
,因为它们是完全相同的对象。
但是,cons
是使用类构造函数构造的。尽管参数是同一个字符串常量,但构造函数为 分配了新内存,尽管包含相同的数据,但这cons
意味着cons
与lit1
和 的对象lit2
不同。
当然,由于三个字符串都包含相同的字符数据,使用该equals
方法会返回true。
(当然,两种类型的字符串构造都是不可变的)
可变性的教科书定义可能会发生变化或变更。在编程中,我们使用这个词来表示其状态可以随时间改变的对象。不可变值恰恰相反——它被创建后,就永远不会改变。
如果这看起来很奇怪,请允许我提醒您,我们一直使用的许多值实际上是不可变的。
var statement = "I am an immutable value";
var otherStr = statement.slice(8, 17);
我想没有人会惊讶地发现第二行不会改变语句中的字符串。事实上,没有字符串方法改变它们操作的字符串,它们都返回新的字符串。原因是字符串是不可变的——它们不能改变,我们只能创建新的字符串。
字符串并不是 JavaScript 中唯一的不可变值。数字也是不可变的。你能想象这样一个环境,其中计算表达式 2 + 3 会改变数字 2 的含义吗?这听起来很荒谬,但我们一直在用我们的对象和数组来做这件事。