Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/security/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 我是否需要构造一个带有*非final*但不可变字段的不可变类?_Java_Security_Immutability_Encapsulation_Defensive Copy - Fatal编程技术网

Java 我是否需要构造一个带有*非final*但不可变字段的不可变类?

Java 我是否需要构造一个带有*非final*但不可变字段的不可变类?,java,security,immutability,encapsulation,defensive-copy,Java,Security,Immutability,Encapsulation,Defensive Copy,如果想要构造一个不可变的类,就不应该公开对非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

如果想要构造一个不可变的类,就不应该公开对非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)
    }
}

我认为没有必要。即使在文件中也提到:

字符串是常量;它们的值在更改后不能更改 创建。因为字符串 对象是不可变的,它们可以共享

因此,一旦创建了字符串对象,它的值就永远不会更改。如果我们想“更改”变量的值,将创建一个新的字符串对象。例如,在
toUpperCase
方法中,原始字符串保持不变,但会创建一个新的副本

编辑:

在考虑字符串时,文本被放入共享池,这意味着:

String h = "HELLO";
String h1 = "HELLO";
s1
s2
均指同一对象

您可以尝试以下代码返回
true

String h = "HELLO";
String h1 = "HELLO";
boolean r = (h==h1);
System.out.println(r);
但是,您可以使用反射更改
字符串的值:

java.lang.reflect.Field valueField = String.class.getDeclaredField("value");
valueField.setAccessible(true);
valueField.set("Original", "Modified".toCharArray()); 

我认为没有必要。即使在文件中也提到:

字符串是常量;它们的值在更改后不能更改 创建。因为字符串 对象是不可变的,它们可以共享

因此,一旦创建了字符串对象,它的值就永远不会更改。如果我们想“更改”变量的值,将创建一个新的字符串对象。例如,在
toUpperCase
方法中,原始字符串保持不变,但会创建一个新的副本

编辑:

在考虑字符串时,文本被放入共享池,这意味着:

String h = "HELLO";
String h1 = "HELLO";
s1
s2
均指同一对象

您可以尝试以下代码返回
true

String h = "HELLO";
String h1 = "HELLO";
boolean r = (h==h1);
System.out.println(r);
但是,您可以使用反射更改
字符串的值:

java.lang.reflect.Field valueField = String.class.getDeclaredField("value");
valueField.setAccessible(true);
valueField.set("Original", "Modified".toCharArray()); 

从技术上讲,如果您真的想在Java中使用一个不可变的类,那么您必须确保在创建类的实例后不能对其进行更改。因此,它的所有字段都可以是最终字段,例如,如果它们通过getter“公开”给世界,那么这些字段本身必须是不可变的(就像字符串一样),或者不能返回到外部世界(保持私有并在getter中创建它们的防御副本),因此原始字段值保持不变。这种不变性也不能因为继承此类而被破坏

你可以阅读约书亚·布洛赫(Joshua Bloch)的《高效Java》一书,或者在互联网上做一些笔记,比如

关于您最近更新的帖子,这里有一个建议,可以确保初始化只进行一次:

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
}

从技术上讲,如果您真的想在Java中使用一个不可变的类,那么您必须确保在创建类的实例后不能对其进行更改。因此,它的所有字段都可以是最终字段,例如,如果它们通过getter“公开”给世界,那么这些字段本身必须是不可变的(就像字符串一样),或者不能返回到外部世界(保持私有并在getter中创建它们的防御副本),因此原始字段值保持不变。这种不变性也不能因为继承此类而被破坏

你可以阅读约书亚·布洛赫(Joshua Bloch)的《高效Java》一书,或者在互联网上做一些笔记,比如

关于您最近更新的帖子,这里有一个建议,可以确保初始化只进行一次:

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
}

这个类是否是不可变的并不重要(对于任何不可变的定义)。特别是,引用
s
是否更改为指向不同的字符串并不重要。字符串对象是不可变的,因此不需要复制它。如果没有防御性复制,
getS
的调用方将获得对
Test
的方法和
getS
的其他调用方使用的相同字符串对象的引用。这并不重要,因为他们对这个字符串所做的任何事情都不会影响其他引用。那是浪费时间和记忆


我忽略了反射。恶意使用这种反射的代码几乎可以破坏任何东西,并且不是偶然编写的,也不是难以发现的。担心这种情况甚至一点都不实际。

不管这个类是否是不可变的(对于不可变的任何定义)。特别是,引用
s
是否更改为指向不同的字符串并不重要。字符串对象是不可变的,因此不需要复制它。如果没有防御性复制,
getS
的调用方将获得对
Test
的方法和
getS
的其他调用方使用的相同字符串对象的引用。这并不重要,因为他们对这个字符串所做的任何事情都不会影响其他引用。那是浪费时间和记忆


我忽略了反射。恶意使用这种反射的代码几乎可以破坏任何东西,并且不是偶然编写的,也不是难以发现的。担心这种情况甚至一点都不实际。

理论上,通过不安全的发布,可以看到
测试
类的实例,其中包含未初始化(
null
s
,也可以看到初始化正确的
s
。这可以通过使
s
易失性来解决

然而,如果您遇到了这样的回调,我想您应该再看看您的设计


如果要使类
可序列化
,则会有更多的问题。

理论上,通过不安全的发布,可以看到
测试类的实例具有未初始化(
null
s
,也可以使用正确初始化的
s
。这可以通过使
s
易失性来解决

然而,如果你有一些回调像t