这是Java Concurrency in Practice
中的一句话共享只读对象包括不可变和有效不可变 对象。
不可变和有效不可变对象之间有什么区别?
答案 0 :(得分:15)
不可扩展且其字段全部为final
且本身不可变的类的实例是不可变的。
由于其方法的细节而无法变异的类的实例实际上是不可变的。例如:
final class C {
final boolean canChange;
private int x;
C(boolean canChange) { this.canChange = canChange; }
public void setX(int newX) {
if (canChange) {
this.x = newX;
} else {
throw new IllegalStateException();
}
}
}
C
的某些实例实际上是不可变的,有些则不是。
另一个例子是零长度数组。它们实际上是不可变的,即使它们的包含类不可证明是不可变的,因为它们中没有可以更改的元素。
Joe-E使用验证程序来证明某些类只允许不可变的实例。标记有Immutable
标记界面的任何内容都会被检查,而String
之类的某些类(因其char[]
无法逃脱而有效不可变)会被视为不可变的。
Joe-E: A Security-Oriented Subset of Java说
由Joe-E库定义的Immutable接口, 特别用语言对待:Joe-E veri fi er 检查实现此接口的每个对象都将 (深度)不可变,并且如果,则引发编译时错误 这不能自动验证。
答案 1 :(得分:8)
这是我通过谷歌搜索并找到this article的理解。一个有效不可变的对象是一个包含可以变异的字段的对象,但它不会让任何东西改变这些字段,因为它永远不会给你一个引用。例如,假设您创建了一个包含ArrayList
的类。 ArrayList
是可变的,但如果你的类总是返回一个ArrayList的副本,并且你的类中的其他所有内容都是不可变的,那么你的类已经有效地不可变:没有办法改变它你班级实例的状态。
博客文章将此作为有效不可变类的示例:
import java.awt.*;
public class Line {
private final Point start;
private final Point end;
public Line(final Point start, final Point end) {
this.start = new Point(start);
this.end = new Point(end);
}
public void draw() {
//...
}
public Point getStart() {
return new Point(start);
}
public Point getEnd() {
return new Point(end);
}
}
Point
个对象是可变的,但没关系,因为这个类没有给任何人直接引用它的点实例。相反,它返回一个具有相同值的新实例。这样,没有人可以改变Line
类的状态。这使得Line
类有效地不可变。
那么这与真正的不可变类有什么不同呢?一个真正不可变的类具有也是不可变的字段。让我们想象Line
是真正不可改变的。要做到这一点,我们还必须想象Point
是不可变的。做出这些假设,getStart()
方法可以这样编写:
public Point getStart() {
return start;
}
答案 2 :(得分:2)
看一下这个答案:
有效不可变和不可变有效不可变和不可变之间的区别在于,在第一种情况下,您仍然需要以安全的方式发布对象。对于不需要的真正不可变对象。所以真正的不可变对象是首选,因为它们更容易发布,我上面说的原因说明了为什么你可能更喜欢不同步的发布。
答案 3 :(得分:1)
不可变对象完全封装了它们的内部状态,并且它们不允许在构造之后修改该状态(可能使用final等),因此它们在多个线程之间共享是安全的,因为从共享对象读取不会对多线程。
有效的不可变对象可能会在多个线程之间共享之前改变它们的状态,但在它们被“发布”之后(即多个引用被赋予多个线程),它们可以保护自己免受修改。
不可变对象阻止您使用有用的软件工程实践,例如延迟初始化,因为为了延迟属性或字段,它们必须是可变的,违反了它们的无忧无虑的并发共享属性。有效的不可变对象放松了这些约束,通过仔细地知道什么时候可以安全地修改它们的内部状态以及什么时候被禁止来充分利用这两种方法。