如果想要构造一个不可变的类,那么不应该将引用暴露给非final字段 - 但是对于像Strings这样的不可变对象呢?
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)
}
}
答案 0 :(得分:2)
理论上,通过不安全的发布可以查看Test
类的实例,其中包含未初始化的null
} s
,也可以通过正确初始化{{1 }}。这可以通过制作s
s
来解决。
但是,如果你发生了类似的回调,我想你想再看一下你的设计。
如果你要上课volatile
那么你还会遇到更多问题。
答案 1 :(得分:1)
我认为没必要。即使在文档中说:
字符串是不变的;他们的价值观无法改变 创建。因为String 对象是不可变的,可以共享。
因此,一旦创建了String对象,其值就永远不会改变。如果我们想要“更改”变量的值,则会创建一个新的String对象。例如在toUpperCase
方法中,原始字符串不变,但创建了新副本。
修改强>
在考虑字符串时,文字被放入共享池中,这意味着:
String h = "HELLO";
String h1 = "HELLO";
s1
和s2
都指向同一个对象。
您可以尝试以下代码返回true
:
String h = "HELLO";
String h1 = "HELLO";
boolean r = (h==h1);
System.out.println(r);
但是,您可以使用反射更改String
值的值:
java.lang.reflect.Field valueField = String.class.getDeclaredField("value");
valueField.setAccessible(true);
valueField.set("Original", "Modified".toCharArray());
答案 2 :(得分:1)
从技术上讲,如果你真的想要一个Java中的不可变类,你必须确保你的类的一个实例在创建之后不能被更改。因此,它的所有字段都可以是最终字段,如果它们通过getter“暴露”到世界,例如,那些字段必须是自身不可变的(如字符串所示)或不返回外部世界(保持私密并创建防御性副本)它们在getter中),因此原始字段值保持不变。这种不变性也不容易因继承这个类而被打破。
你可以在有效的Java中阅读更多关于它的信息 - 约书亚布洛赫的书,或者从互联网上做一些笔记,比如from here。
关于您最近对该帖子的更新,这里有一个建议,确保只进行一次初始化:
private String s; // CAN'T MAKE THIS FINAL
private boolean stringWasSet = false;
public void onCreate(String s) { // a callback called ONCE after construction
if (!stringWasSet) {
this.s = s; // No need for defensive copy here, if the variable itself is immutable, like String
stringWasSet = true;
}
}
public String getS() {
return s; // No need for defensive copy here, if the variable itself is immutable, like String
}
答案 3 :(得分:1)
这个类是否是不可变的(对于任何不可变的定义)并不重要。特别是,引用s
是否被更改为指向不同的字符串并不重要。 字符串对象是不可变的,因此您无需复制它。如果没有防御性复制,getS
的调用者将获得Test
方法和getS
的其他调用者使用的相同字符串对象的引用。这没关系,因为他们对这个字符串的任何 1 都会影响其他指示对象。这是浪费时间和记忆。
1 我忽略了反思。恶意使用这样的反射的代码几乎可以破坏任何东西,并且不是偶然写入或难以发现的。担心这种情况甚至不太可行。