Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/380.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

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/sorting/2.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_Immutability - Fatal编程技术网

Java 字段是否需要显式为final才能有一个";“适当的”;不可变对象?

Java 字段是否需要显式为final才能有一个";“适当的”;不可变对象?,java,immutability,Java,Immutability,您经常阅读有关不可变对象的内容,这些对象要求Java中的最终字段是不可变的。事实上是这样吗?或者仅仅是没有公共的可变性就足够了,而不是真正地改变状态 例如,如果您有一个由构建器模式构建的不可变对象,那么您可以在构建时让构建器分配各个字段,或者让构建器保存字段本身,并通过将值传递给其(私有)构造函数最终返回不可变对象 使用final字段具有防止实现错误的明显优势(例如允许代码保留对构建器的引用并多次“构建”对象,同时实际上对现有对象进行了修改),但是让构建器在构建对象时将其数据存储在对象中似乎是一

您经常阅读有关不可变对象的内容,这些对象要求Java中的最终字段是不可变的。事实上是这样吗?或者仅仅是没有公共的可变性就足够了,而不是真正地改变状态

例如,如果您有一个由构建器模式构建的不可变对象,那么您可以在构建时让构建器分配各个字段,或者让构建器保存字段本身,并通过将值传递给其(私有)构造函数最终返回不可变对象

使用final字段具有防止实现错误的明显优势(例如允许代码保留对构建器的引用并多次“构建”对象,同时实际上对现有对象进行了修改),但是让构建器在构建对象时将其数据存储在对象中似乎是一种简单的方法


因此,问题是:假设构建器没有提前泄漏对象,并且在构建后停止修改对象(比如通过将其对对象的引用设置为null),那么“不变性”中是否真的获得了什么(如改进的线程安全性)如果对象的字段改为最终字段,则对象的属性是什么?

对象当然可以具有可变的私有字段,并且仍然作为不可变的对象工作。要满足不变性契约,最重要的是对象从外部看起来是不变性的。例如,具有非最终私有字段但没有设置器的对象将满足此要求

事实上,如果您的封装是正确的,那么您实际上可以改变内部状态,并且仍然可以作为“不可变”对象成功地运行。例如,数据结构的某种惰性计算或缓存

例如,Clojure在其惰性序列的内部实现中就是这样做的,这些对象的行为就像它们是不可变的一样,但只有在直接请求它们时才实际计算和存储未来的值。任何后续请求都将检索存储的值

然而,我想补充一点,作为一个警告,您实际上想要改变不可变对象内部结构的地方可能非常少。如果有疑问,请将其设置为最终版本。

是的,您确实可以从
final
字段中获得“线程安全性”。也就是说,在构造过程中分配给
final
字段的值保证对所有线程都可见。线程安全的另一种替代方法是声明字段
volatile
,但是每次读取都会产生很高的开销……这会让任何查看类并想知道为什么这个“不可变”类的字段标记为“volatile”的人感到困惑


标记字段
final
在技术上是最正确的,并且最清楚地表达了您的意图。不幸的是,它确实使构建器模式非常麻烦。我认为应该可以创建一个注释处理器来合成一个不可变类的生成器,就像ProjectLombok使用setter和getter所做的那样。真正的工作将是IDE支持,这样你就可以对不存在的建设者进行编码。

< P>我想你只需要考虑它运行的环境,并决定使用反射来操纵对象的框架是否是危险的。
你可以很容易地编造一个奇怪的场景,假设不可变的对象通过注入后攻击受到攻击,因为web绑定框架被配置为使用反射而不是bean setter。

你肯定可以拥有一个带有非final字段的不可变对象

例如,请参见java.lang.String的java 1.6实现。

注释: @埃里克森

就像这样:

class X { volatile int i, j; } X y; // thread A: X x = new X; x.i = 1; x.j = 2; y = x; // thread B: if (y != null) { a = y.i; b = y.j; } 类X{volatile int i,j;} xy; //线程A: X=新X; x、 i=1; x、 j=2; y=x; //线程B: 如果(y!=null){ a=y.i; b=y.j; }

“例如,具有非最终私有字段但没有设置器的对象将满足此要求”-这本身并不保证不变性。getter可以返回数据成员引用,调用方可以修改这些引用。@Eyal:true-尽管引用本身是不可变的。如果您使用不可变成员构造对象,那么不可变性将一直保持不变。或者,创建一个不可变的组合或可变对象的集合在某些上下文中是有意义的。。。我的定义是“从观察者的角度看不可变”,我认为这是最有用的,但我也听说人们将不可变定义为绝对不允许任何改变。@mikera:我更喜欢的定义是,如果一个对象是不可变的,将任何特定字段的内容替换为写入该字段的任何值,或对字段的任何组合执行类似操作,不应影响其有效性或状态,但可能会影响is性能。@curiousguy:
volatile
将确保一个线程写入的值对另一个线程可见。例如,如果没有它,读卡器可能无法清除缓存在寄存器中的值并将其读取到主内存。“线程安全的另一种选择是声明字段
volatile
”你能描述一下这样做的正确方法吗?@erickson-一个类,它的字段既不是
final
也不是
volatile
,只要这些字段在构造过程中完全初始化,并且在构建之后保持不变,它可能仍然是线程安全的。构造后,内存中存在一个完全成形的对象,对该对象的任何引用都必须引用完全成形的对象。因为它不会改变,所以不可能看到过时的值。这样的对象必须“安全发布”。(例如,新对象被分配给一个
volatile
变量,该变量由其他线程读取。)只需避免字段突变就可以了