调用java.lang.String immutable是否正确?

2022-09-03 12:55:56

这个Java教程说,不可变对象在创建后不能更改其状态。

java.lang.String有一个字段

/** Cache the hash code for the string */
private int hash; // Default to 0

它在方法的第一次调用时初始化,因此它在创建后会更改:hashCode()

    String s = new String(new char[] {' '});
    Field hash = s.getClass().getDeclaredField("hash");
    hash.setAccessible(true);
    System.out.println(hash.get(s));
    s.hashCode();
    System.out.println(hash.get(s));

输出

0
32

调用不可变是否正确?String


答案 1

更好的定义不是对象没有变化,而是不能观察到它已被改变。它的行为永远不会改变:将始终为该字符串ditto和所有其他方法返回相同的内容。.substring(x,y)equals

该变量在您首次调用时计算,并缓存以供进一步调用。这基本上就是他们在函数式编程语言中所说的“记忆化”。.hashcode()

反射并不是真正用于“编程”的工具,而是用于元编程(即用于生成程序的编程程序),因此它并不真正重要。这相当于使用内存调试器更改常量的值。


答案 2

“不可变”一词含糊不清,无法进行精确的定义。

我建议阅读Eric Lippert博客中的各种不变性。虽然从技术上讲,这是一篇C#文章,但它与提出的问题非常相关。特别:

观测不可变性:

假设你有一个对象,它的属性是,每次你调用一个方法,查看一个字段,等等,你会得到相同的结果。从调用方的角度来看,这样的对象是不可变的。但是,您可以想象,在后台,对象正在执行延迟初始化,在哈希表中记住函数调用的结果等。对象的“内脏”可能是完全可变的。

这有什么关系?真正深度不可变的对象永远不会改变它们的内部状态,因此本质上是线程安全的。在幕后具有可变对象可能仍然需要具有复杂的线程代码,以便在“同时”在两个线程上调用该对象时保护其内部可变状态免受损坏。