java我是否需要构造一个带有*非final*但不可变字段的不可变类?
如果想要构造一个不可变的类,就不应该公开对非final字段的引用,但即使对于字符串之类的不可变对象也是如此
public final class Test { // Test class is meant to be immutable
private String s; // CAN'T MAKE THIS FINAL
void onCreate(String s) { // a callback called ONCE after construction
this.s = new String(s); // do I need to do this ? (protect me from me)
}
public String getS() {
return new String(s); //do I need to do this ?(protect me from the world)
}
}
# 1 楼答案
理论上,通过不安全的发布,可以看到}来解决
Test
类的实例具有未初始化(null
)s
,也可以看到具有正确初始化的s
。这可以通过使s
{然而,如果您遇到了这样的回调,我想您应该再看看您的设计
如果要使类
Serializable
,则会有更多的问题# 2 楼答案
从技术上讲,如果您真的想在Java中使用一个不可变的类,那么您必须确保在创建类的实例后不能对其进行更改。因此,它的所有字段都可以是最终字段,例如,如果它们通过getter“公开”给世界,那么这些字段本身必须是不可变的(就像字符串一样),或者不能返回到外部世界(保持私有并在getter中创建它们的防御副本),因此原始字段值保持不变。这种不变性也不能因为继承此类而被破坏
你可以在约书亚·布洛赫(Joshua Bloch)的一本书《高效Java》中阅读更多关于它的内容,或者从互联网上做一些笔记,比如from here
关于您最近更新的帖子,这里有一个建议,可以确保初始化只进行一次:
# 3 楼答案
这个类是否是不可变的并不重要(对于任何不可变的定义)。特别是,引用
s
是否更改为指向不同的字符串并不重要。字符串对象是不可变的,因此不需要复制它。如果没有防御性复制,getS
的调用者将获得对Test
的方法和getS
的其他调用者使用的相同字符串对象的引用。这并不重要,因为它们对该字符串所做的任何操作都不会影响其他引用。那是浪费时间和记忆1我忽略了反射。恶意使用这种反射的代码几乎可以破坏任何东西,并且不是偶然编写的,也不是难以发现的。担心这个案子一点也不现实
# 4 楼答案
我认为没有必要。即使在文件中也提到:
因此,一旦创建了字符串对象,它的值就永远不会更改。如果我们想“更改”变量的值,将创建一个新的字符串对象。例如在
toUpperCase
方法中,原始字符串保持不变,但会创建一个新的副本编辑:
在考虑字符串时,文本被放入共享池,这意味着:
s1
和s2
都指同一个对象您可以尝试以下代码返回
true
:但是,您可以使用反射更改
String
的值: