不可变类?

怎样才能使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实例属性来避免重新分配
  • 使用一个类接口,根本不允许任何能够修改你的类中的内容的操作(只是gettersetter

不变性的优点是您可以对这些对象做出的假设:

  • 你获得了无副作用的规则(这在function性编程语言中非常stream行),并且允许你在并发环境中更容易地使用对象,因为你知道当它们不能被primefaces或非primefaces的方式改变时被许multithreading使用
  • 语言的实现可以以不同的方式处理这些对象,将它们放置在用于静态数据的内存区域中,从而更快,更安全地使用这些对象(这是JVM内部为string所发生的情况)
  • 什么是不可变的?

简而言之,不可变类是一个实例不能被修改的类。 包含在每个不可变对象中的信息在创build时被提供,并且在其生命周期中被冻结。 一旦创build了一个不可变的对象,它就是只读的,永远不变,就像一个化石。

  • 如何使一个不变的

    1. 使所有领域的私人和最终。
    2. 不要提供任何修改对象状态的方法。
    3. 确保课程无法扩展。
    4. 确保独占访问任何可变字段

优点

  1. 不可变对象是线程安全的,所以你不会有任何同步问题。
  2. 不可变的对象是好的Map键和Set元素,因为这些元素一旦创build就不会改变。
  3. 不变性使得编写,使用和推理代码变得更容易(类不变是一次build立,然后保持不变)
  4. 由于不存在对象之间的冲突,不可变性使并行化程序变得更容易。
  5. 即使您有例外情况,程序的内部状态也是一致的。
  6. 对不可变对象的引用可以被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一个不可变的类,你应该遵循以下步骤:

  1. 让你的课最后决定,以便其他课不能扩展它。
  2. 使所有的字段都是最终的,以便它们在构造函数中只被初始化一次,之后再不被修改。
  3. 不要暴露setter方法。
  4. 暴露修改类的状态的方法时,您必须始终返回该类的新实例。
  5. 如果这个类拥有一个可变的对象:

在构造函数中,确保使用传入参数的克隆副本,并且永远不要将可变字段设置为通过构造函数传递的实例,这是为了防止通过该对象的客户端之后进行修改。

确保始终返回该字段的克隆副本,并且永远不会返回实际的对象实例。

问:不可变类的好处?

线程安全

参考: 如何在java中创build一个不可变的类

作为非英语母语者,我不喜欢“不可变类”的普遍解释是“构造的类对象是不可变的”。 相反,我自己倾向于把“类对象本身是不可改变的”解释为:

这就是说,“不可变的类”是一种不可变的对象。 不同之处在于回答什么是好处。 就我的知识/解释而言,不可变的类可防止其对象的运行时行为被修改。