这可能是有史以来问过的最愚蠢的问题,但我认为对于一个Java新手来说,这相当令人困惑。
一个很好的例子(在Java中)将非常感谢。
实际上,如果使用上面建议的维基百科定义,字符串并不是不变的。
String的状态确实会改变后构造。看看hashcode()方法。String将hashcode值缓存在本地字段中,但直到hashcode()的第一次调用才计算它。这种对hashcode的懒惰评估将String置于一个有趣的位置,作为状态改变的不可变对象,但如果不使用反射,就无法观察到它已经改变。
所以不可变的定义应该是一个不能被观察到已经改变的对象。
如果一个不可变对象在创建后状态发生了变化,但是没有人能看到它(没有反射),这个对象仍然是不可变的吗?
不可变对象是内部字段(或至少是影响其外部行为的所有内部字段)无法更改的对象。
不可变字符串有很多优点:
性能:执行以下操作:
String substring = fullstring.substring(x,y);
substring()方法的底层C可能是这样的:
// Assume string is stored like this:
struct String { char* characters; unsigned int length; };
// Passing pointers because Java is pass-by-reference
struct String* substring(struct String* in, unsigned int begin, unsigned int end)
{
struct String* out = malloc(sizeof(struct String));
out->characters = in->characters + begin;
out->length = end - begin;
return out;
}
请注意,不必复制任何字符!如果字符串对象是可变的(字符以后可能会更改),则必须复制所有字符,否则子字符串中字符的更改将在以后反映在另一个字符串中。
并发性:如果不可变对象的内部结构有效,它将始终有效。不同的线程不可能在该对象中创建无效状态。因此,不可变对象是线程安全的。
垃圾收集:垃圾收集器更容易对不可变对象做出逻辑决策。
然而,不变性也有缺点:
性能:等等,我以为你说性能是不变性的优势!有时候是这样,但并不总是这样。以下面的代码为例:
foo = foo.substring(0,4) + "a" + foo.substring(5); // foo is a String
bar.replace(4,5,"a"); // bar is a StringBuilder
这两行都用字母“a”替换了第四个字符。第二段代码不仅更容易阅读,而且速度更快。看看你必须如何为foo做底层代码。子串很容易,但是现在因为空间五已经有一个字符,并且其他东西可能引用foo,你不能仅仅改变它;你必须复制整个字符串(当然,其中一些功能被抽象成真正的底层C中的函数,但是这里的重点是展示在一个地方执行的代码)。
struct String* concatenate(struct String* first, struct String* second)
{
struct String* new = malloc(sizeof(struct String));
new->length = first->length + second->length;
new->characters = malloc(new->length);
int i;
for(i = 0; i < first->length; i++)
new->characters[i] = first->characters[i];
for(; i - first->length < second->length; i++)
new->characters[i] = second->characters[i - first->length];
return new;
}
// The code that executes
struct String* astring;
char a = 'a';
astring->characters = &a;
astring->length = 1;
foo = concatenate(concatenate(slice(foo,0,4),astring),slice(foo,5,foo->length));
请注意,concatenate被调用两次,这意味着整个字符串必须循环通过!将此与bar
操作的C代码进行比较:
bar->characters[4] = 'a';
可变字符串操作显然要快得多。
结论:在大多数情况下,您需要一个不可变的字符串。但是如果您需要在字符串中添加和插入大量内容,您需要可变性来提高速度。如果您想要并发安全和垃圾回收机制的好处,关键是将您的可变对象保持在方法的本地:
// This will have awful performance if you don't use mutable strings
String join(String[] strings, String separator)
{
StringBuilder mutable;
boolean first = true;
for(int i = 0; i < strings.length; i++)
{
if(!first) first = false;
else mutable.append(separator);
mutable.append(strings[i]);
}
return mutable.toString();
}
因为可变对象是一个本地引用,所以你不必担心并发安全(只有一个线程接触过它)。而且因为它没有在其他任何地方被引用,所以它只在堆栈上被分配,所以一旦函数调用完成,它就会被解除分配(你不必担心垃圾回收机制)。你可以获得可变性和不可变性的所有性能优势。
不可变意味着一旦对象的构造函数完成执行,就不能更改该实例。
这很有用,因为这意味着您可以传递对对象的引用,而不用担心其他人会更改其内容。尤其是在处理并发性时,永远不会更改的对象没有锁定问题
e. g.
class Foo
{
private final String myvar;
public Foo(final String initialValue)
{
this.myvar = initialValue;
}
public String getValue()
{
return this.myvar;
}
}
Foo
不必担心getValue()
的调用方可能会更改字符串中的文本。
如果你想象一个类似于Foo
的类,但是成员是StringBuilder
而不是String
,你可以看到getValue()
的调用方能够改变Foo
实例的StringBuilder
属性。
还要注意你可能会发现的不同类型的不变性:埃里克·利珀特写了一篇关于这一点的博客文章。基本上,您可以拥有接口不可变但在幕后实际可变的私有状态(因此不能在线程之间安全共享)的对象。
问题内容: 我在不可变字符串上编写了以下代码。 输出: 此处变量的值a已更改(许多人说不能更改不可变对象的内容)。但是,一句话到底是什么String是不变的呢? 问题答案: 在进一步探讨不变性之前,让我们在得出任何结论之前先了解一下类及其功能。 这是如何工作的: 与往常一样,这将创建一个包含的字符串”knowledge”并为其指定一个reference str。很简单?让我们执行更多功能: 让我们
正如你可以看到在这种情况下,虽然我们更改了的值,的值没有改变。 这是因为的类型,是不可变的。 在JavaScript内置类型中,以下是不可变的: Boolean Number Symbol Null Object Array String是一种不常见的情况,因为它可以使用进行迭代,并像数组一样提供数字索引器,但会执行以下操作:
问题内容: 我只是想使用Django中的url标记与另一个页面建立简单的连接。我收到“设置对象不可逆”的错误。搜索了一下之后,我一直找不到任何东西。 urls.py admin_view.html 我以前没有遇到过这个问题,而且看起来只是我要解决的一个简单问题。任何帮助表示赞赏。 问题答案: 应该是一个清单。您目前有一套。它应该是: 在模板中,当网址格式名称为字符串时,应使用引号: 如果要使用名称
当使用代码进行扩展时,我得到以下错误,我不确定他们是要求使用不同的操作符还是修改基于互联网搜索的表达式中的值。 错误:%不可用:请改用trunchingRemaider 扩展代码: 设置分钟和秒变量时出错。
本文向大家介绍String 为什 么是不可变的?相关面试题,主要包含被问及String 为什 么是不可变的?时的应答技巧和注意事项,需要的朋友参考一下 简单的来说:String 类中使用 final 关键字修饰字符数组来保存字符串,`private final char value[]`,所以 String 对象是不可变的。而StringBuilder 与 StringBuffer 都继承自 Ab
问题内容: 作为Python的初级开发人员,我已经在控制台中多次看到此错误消息,但是我不完全了解它的含义。 谁能以一般的方式告诉我,哪种操作会产生此错误? 问题答案: 当您尝试使用 无法调用的对象时,会发生该错误。 可调用对象可以是函数或类(实现方法)。根据Python Docs : object . call (self [,args …]) :当实例被“调用”为函数时调用 例如: 不是可调用的