不可变类?
怎样才能使Java类不可变,什么是不变性的需要,使用它有什么好处?
什么是不可变对象?
一个不可变对象是一个在实例化后不会改变状态的对象。
如何使一个对象不可变?
一般来说,一个不可变对象可以通过定义一个没有任何成员暴露的类来实现,并且没有任何setter。
下面的类将创build一个不可变的对象:
class ImmutableInt { private final int value; public ImmutableInt(int i) { value = i; } public int getValue() { return value; } }
从上面的例子可以看出, ImmutableInt
的值只能在实例化对象时设置,并且只有一个getter( getValue
)对象的状态在实例化后才能被改变。
但是,必须注意,对象所引用的所有对象也必须是不可变的,否则可能会改变对象的状态。
例如,允许通过getter获取对数组或ArrayList
的引用,将允许通过更改数组或集合来更改内部状态:
class NotQuiteImmutableList<T> { private final List<T> list; public NotQuiteImmutableList(List<T> list) { // creates a new ArrayList and keeps a reference to it. this.list = new ArrayList(list); } public List<T> getList() { return list; } }
上面的代码的问题是ArrayList
可以通过getList
获得getList
操纵,导致对象本身的状态被改变,因此不是不可变的。
// notQuiteImmutableList contains "a", "b", "c" List<String> notQuiteImmutableList= new NotQuiteImmutableList(Arrays.asList("a", "b", "c")); // now the list contains "a", "b", "c", "d" -- this list is mutable. notQuiteImmutableList.getList().add("d");
解决此问题的一种方法是从getter调用时返回数组或集合的副本:
public List<T> getList() { // return a copy of the list so the internal state cannot be altered return new ArrayList(list); }
不变性的优点是什么?
不变性的优势来自并发性。 在可变对象中维护正确性是困难的,因为多个线程可能试图改变同一对象的状态,导致一些线程看到相同对象的不同状态,这取决于读和写的时间目的。
通过拥有一个不可变对象,可以确保所有看着该对象的线程都将看到相同的状态,因为不可变对象的状态不会改变。
除了已经给出的答案之外,我build议阅读Effective Java,2nd Ed的不变性,因为有些细节很容易被忽略(例如防御性副本)。 另外,有效的Java第二版。 是每个Java开发人员必读的。
你可以像这样创build一个不可变的类:
public final class Immutable { private final String name; public Immutable(String name) { this.name = name; } public String getName() { return this.name; } // No setter; }
不可变的类是有用的,因为它们是线程安全的。 他们还对你的deviseexpression了深刻的印象:“不能改变这一点”。 当它适用时,这正是你所需要的。
Q1)什么是不可变的类和对象?
Ans)不可变类是一个曾经创build过的类,它的内容是不能改变的。 不可变的对象是一旦构build就不能改变状态的对象。 例如String类
Q2)如何创build一个不可变的类?
Ans)要创build一个不可变的类,请遵循以下步骤:
-
创build一个最终课程。
-
只使用构造函数设置属性的值。
-
使类的属性是最终的和私有的
-
不要为这些属性提供任何setter。
-
如果实例字段包含对可变对象的引用,则不允许更改这些对象:
-
不要提供修改可变对象的方法。
-
不要共享对可变对象的引用。 切勿将引用存储到传递给构造函数的外部可变对象; 如有必要,创build副本,并存储对副本的引用。 同样,必要时创build内部可变对象的副本,以避免在方法中返回原件。
-
不可变类在实例化后不能重新赋值。构造函数将值赋给它的私有variables。 在对象变为null之前,由于setter方法不可用,所以值不能被改变。
是不可变的应该满足以下,
- Alvariables应该是私有的 。
- 没有提供mutator方法(setter)。
- 避免方法重写,使类最终(强不变性)或方法最终(周不变性)。
- 如果它包含非原始类或可变类,则深度复制。
/** * Strong immutability - by making class final */ public final class TestImmutablity { // make the variables private private String Name; //assign value when the object created public TestImmutablity(String name) { this.Name = name; } //provide getters to access values public String getName() { return this.Name; } }
Advanteges:不可变的对象包含它的初始值,直到它死亡。
Java的不可变类短音符
不变性可以主要通过两种方式实现:
- 使用
final
实例属性来避免重新分配 - 使用一个类接口,根本不允许任何能够修改你的类中的内容的操作(只是getter和setter
不变性的优点是您可以对这些对象做出的假设:
- 你获得了无副作用的规则(这在function性编程语言中非常stream行),并且允许你在并发环境中更容易地使用对象,因为你知道当它们不能被primefaces或非primefaces的方式改变时被许multithreading使用
- 语言的实现可以以不同的方式处理这些对象,将它们放置在用于静态数据的内存区域中,从而更快,更安全地使用这些对象(这是JVM内部为string所发生的情况)
-
什么是不可变的?
简而言之,不可变类是一个实例不能被修改的类。 包含在每个不可变对象中的信息在创build时被提供,并且在其生命周期中被冻结。 一旦创build了一个不可变的对象,它就是只读的,永远不变,就像一个化石。
-
如何使一个不变的
- 使所有领域的私人和最终。
- 不要提供任何修改对象状态的方法。
- 确保课程无法扩展。
- 确保独占访问任何可变字段
优点
- 不可变对象是线程安全的,所以你不会有任何同步问题。
- 不可变的对象是好的Map键和Set元素,因为这些元素一旦创build就不会改变。
- 不变性使得编写,使用和推理代码变得更容易(类不变是一次build立,然后保持不变)
- 由于不存在对象之间的冲突,不可变性使并行化程序变得更容易。
- 即使您有例外情况,程序的内部状态也是一致的。
- 对不可变对象的引用可以被caching,因为它们不会改变。
作为Java中的一个良好的编程习惯,应尽可能使用不可变对象。 不变性可能会带来性能成本,因为当一个对象不能被改变的时候,如果我们想要写一个对象的话,我们需要复制它。 当你关心性能(比如编程一个游戏)时,可能需要使用一个可变对象。 即使这样,通常也会限制对象的可变性。
-
缺点
由于不可变对象不能被重用,它们只是一个使用和抛出。 string是一个主要的例子,它可以创build大量的垃圾,并可能由于垃圾收集沉重而潜在地减慢应用程序。
不可变类是那些创build后不能更改对象的类 。
不可变的类是有用的
- caching目的
-
并发环境(ThreadSafe)
-
很难inheritance
- 在任何环境中都不能更改值
例
string类
代码示例
public final class Student { private final String name; private final String rollNumber; public Student(String name, String rollNumber) { this.name = name; this.rollNumber = rollNumber; } public String getName() { return this.name; } public String getRollNumber() { return this.rollNumber; } }
另一种使不可变对象的方法是使用Immutables.org库:
假设添加了所需的依赖关系,请使用抽象访问器方法创build一个抽象类。 您可以通过使用接口或注释(@interface)进行注释来执行相同的操作:
package info.sample; import java.util.List; import java.util.Set; import org.immutables.value.Value; @Value.Immutable public abstract class FoobarValue { public abstract int foo(); public abstract String bar(); public abstract List<Integer> buz(); public abstract Set<Long> crux(); }
现在可以生成并使用生成的不可变实现:
package info.sample; import java.util.List; public class FoobarValueMain { public static void main(String... args) { FoobarValue value = ImmutableFoobarValue.builder() .foo(2) .bar("Bar") .addBuz(1, 3, 4) .build(); // FoobarValue{foo=2, bar=Bar, buz=[1, 3, 4], crux={}} int foo = value.foo(); // 2 List<Integer> buz = value.buz(); // ImmutableList.of(1, 3, 4) } }
问:什么是不可变的类?
一个对象是不可变的,如果它的状态在构造之后不能改变,那么不可变对象不会暴露任何其他对象修改它们的状态的方式,对象的字段在构造器中只被初始化一次,并且不会再次改变。
问:如何创build不可变的类?
为了创build一个不可变的类,你应该遵循以下步骤:
- 让你的课最后决定,以便其他课不能扩展它。
- 使所有的字段都是最终的,以便它们在构造函数中只被初始化一次,之后再不被修改。
- 不要暴露setter方法。
- 暴露修改类的状态的方法时,您必须始终返回该类的新实例。
- 如果这个类拥有一个可变的对象:
在构造函数中,确保使用传入参数的克隆副本,并且永远不要将可变字段设置为通过构造函数传递的实例,这是为了防止通过该对象的客户端之后进行修改。
确保始终返回该字段的克隆副本,并且永远不会返回实际的对象实例。
问:不可变类的好处?
线程安全
参考: 如何在java中创build一个不可变的类
作为非英语母语者,我不喜欢“不可变类”的普遍解释是“构造的类对象是不可变的”。 相反,我自己倾向于把“类对象本身是不可改变的”解释为:
这就是说,“不可变的类”是一种不可变的对象。 不同之处在于回答什么是好处。 就我的知识/解释而言,不可变的类可防止其对象的运行时行为被修改。