不可变类的例子

我已经知道不可变类的定义,但是我需要一些例子。

标准API中的一些着名的不可变类:

  • java.lang.String(已经提到)
  • 基本types的包装类:java.lang.Integer,java.lang.Byte,java.lang.Character,java.lang.Short,java.lang.Boolean,java.lang.Long,java.lang.Double, java.lang.Float中
  • java.lang.StackTraceElement(用于构buildexception堆栈跟踪)
  • 大多数枚举类是不可改变的,但这实际上取决于具体情况。 (不要实现可变的枚举,这将使你搞砸了一些。)我认为标准API中至less所有的枚举类实际上是不可变的。

  • java.math.BigInteger和java.math.BigDecimal(至less是这些类本身的对象,子类可以引入可变性,尽pipe这不是一个好主意)

  • java.io.File中。 请注意,这表示VM(本地系统上的文件)外部的对象,可能存在也可能不存在,并且有一些方法可修改和查询此外部对象的状态。 但是File对象本身保持不变。 (java.io中的所有其他类都是可变的。)

  • java.awt.Font – 表示在屏幕上绘制文本的字体(可能有一些可变的子类,但这肯定不会有用)

  • java.awt.BasicStroke – 用于在graphics上下文上绘制线条的辅助对象
  • java.awt.Color – (至less这个类的对象,一些子类可能是可变的,或者取决于一些外部因素(比如系统颜色)),以及java.awt.Paint的大多数其他实现
    • java.awt.GradientPaint,
    • java.awt.LinearGradientPaint
    • java.awt.RadialGradientPaint,
    • (我不确定关于java.awt.TexturePaint)
  • java.awt.Cursor – 表示鼠标光标的位图(这里也有一些子类可能是可变的,也可能取决于外部因素)

  • java.util.Locale – 代表特定的地理,政治或文化区域

  • java.util.UUID – 尽可能多的全局唯一标识符
  • 虽然大多数集合都是可变的,但是在java.util.Collections类中有一些包装方法,它们在集合上返回一个不可修改的视图。 如果你传给他们一个不为人知的集合,这些实际上是不可变的集合。 此外, Collections.singletonMap() .singletonList.singleton返回不可变的一元集合,而且还有不可变的空集合。

  • java.net.URL和java.net.URI – 表示资源(在互联网上或其他地方)

  • java.net.Inet4Address和java.net.Inet6Address,java.net.InetSocketAddress
  • java.security.Permission的大多数子类(表示某些操作所需的权限或给予某些代码的权限),而不是java.security.PermissionCollection和子类。

人们可以说,原始types也是不变的 – 你不能改变42的价值,对吗?


是AccessControlContext类的一个不可变类

AccessControlContext没有任何变异的方法。 其状态由一个ProtectionDomain(这是一个不可变的类)和一个DomainCombiner组成。 DomainCombiner是一个接口,所以原则上这个实现可以在每次调用时做不同的事情。

实际上,ProtectionDomain的行为也可能取决于当前的现行政策 – 是否将这样一个对象称为不可变的是有争议的。

和AccessController?

没有AccessControllertypes的对象,因为这是一个没有可访问构造函数的最终类。 所有方法都是静态的。 可以说AccessController既不可变也不可变,或者两者兼有。

对于其他所有不能拥有对象(实例)的类也是一样的,最有名的是:

  • java.lang.Void的
  • java.lang.System(但是这有一些可变的静态状态 – outerr
  • java.lang.Math(这也是随机数生成器)
  • java.lang.reflect.Array中
  • java.util.Collections中
  • java.util.Arrays中

不可修改的类在构build后不能更改。 所以,例如,一个Java String是不可变的。

为了使一个类不可变,你必须把它作为final ,所有的字段是privatefinal 。 例如,下面的类是不可变的:

 public final class Person { private final String name; private final int age; private final Collection<String> friends; public Person(String name, int age, Collection<String> friends) { this.name = name; this.age = age; this.friends = new ArrayList(friends); } public String getName() { return this.name; } public int getAge() { return this.age; } public Collection<String> getFriends() { return Collections.unmodifiableCollection(this.friends); } } 

我在代码示例中添加了一个方法,显示如何处理集合,这是一个重要的观点。

在可能的情况下, 你应该让类不变,因为那样你就不必担心线程安全等问题。

记住把类声明为final并不意味着它是“不可变的”,这基本上意味着这个类不能被扩展(或专门化)。

不可变类必须有私有字段和最终字段(没有setter),所以在构造之后,它的字段值就不能改变了。

要创build一个不可变的类,您需要遵循以下步骤:

  1. 将这个类声明为final,所以不能扩展。
  2. 将所有字段设为私有,以便不允许直接访问。
  3. 不要为variables提供setter方法
  4. 使所有可变字段最终,以便它的值只能分配一次。
  5. 通过执行深度复制的构造函数初始化所有的字段。
  6. 在getter方法中执行克隆对象以返回副本而不是返回实际的对象引用。

一个例子可以在这里find。

我们也可以使用Builder Pattern轻松创build不可变的类,可以在这里find一个例子。

Sun(Oracle)文档有一个关于如何创build一个不可变对象的极好的清单。

  1. 不要提供“setter”方法 – 修改字段引用的字段或对象的方法。
  2. 使所有的领域最终和私人。
  3. 不要让子类重写方法。 最简单的方法是将类声明为final。 更复杂的方法是使构造函数保持私有状态,并在工厂方法中构造实例。
  4. 如果实例字段包含对可变对象的引用,则不允许更改这些对象:
    • 不要提供修改可变对象的方法。
    • 不要共享对可变对象的引用。 切勿将引用存储到传递给构造函数的外部可变对象; 如有必要,创build副本,并存储对副本的引用。 同样,必要时创build内部可变对象的副本,以避免在方法中返回原件。

来自: http : //download.oracle.com/javase/tutorial/essential/concurrency/imstrat.html

该网站还提供了在并发环境中使用的示例,但在编写库时,不变性也很有用。 它确保来电者能够只改变我们允许的内容。

String是不可变类的一个很好的“现实世界”例子。 你可以将它与可变的StringBuilder类进行对比。


大部分用于reflection的Java类都是不可变的。 还有一些是“几乎不可变的”:例如,实现Accessible的类只有一个setAccessible方法,用于更改Accessible实例的状态。


我相信标准类库中还有很多。

LocalDateLocalTimeLocalDateTime类(从1.8开始)也是不可变的。 事实上,这是OCAJSE8(1Z0-808)考试的主题,这就是为什么我决定将其视为不仅仅是一个评论。

所有原始的包装类(如布尔字符字节整数浮点 )是不可变的。

货币货币 API(针对Java9)也应该是不可改变的。

顺便提一句, 数组支持的列表 (由Arrays.asList(myArray)创build)在结构上是可以变的。

此外,还有一些边界线案例,如java.util.Optional (在OCP考试中提供,1Z0-809),如果包含的元素本身是不可变的,则它是不可变的。

不可变类是一个曾经创build过的类,它的内容是不能改变的。 不可变的对象是一旦构build就不能改变状态的对象。 示例 – string和所有java包装类。

可变对象是一旦构造就可以改变其状态的对象.example- StringBuffer一旦改变了值改变了的内存位置。 看下面的例子 –

  public static void immutableOperation(){ String str=new String("String is immutable class in Java object value cann't alter once created..."); System.out.println(str); str.replaceAll("String", "StringBuffer"); System.out.println(str); str.concat("Concating value "); System.out.println(str + "HashCode Value " + str.hashCode()); str=str.concat("Concating value "); System.out.println(str + "HashCode Val " + str.hashCode()); } public static void mutableOperation(){ StringBuffer str=new StringBuffer("StringBuffer is mutable class in Java object value can alter once created..."); System.out.println(str + "HashCode Val - " + str.hashCode()); str.replace(0, 12, "String"); System.out.println(str + "HashCode Val - " + str.hashCode()); } 

我喜欢使用具有可变属性的示例。 这有助于理解不可变类如何真正起作用。

可变的类

 class MutableBook { private String title; public String getTitle(){ return this.title; } public void setTitle(String title){ this.title = title; } } 

还有一个使用可变实例的不可变实现。

 public class ImmutableReader { private final MutableBook readersBook; private final int page; public ImmutableReader(MutableBook book) { this(book, 0); } private ImmutableReader(MutableBook book, int page){ this.page = page; // Make copy to ensure this books state won't change. MutableBook bookCopy = new MutableBook(); bookCopy.setTitle(book.getTitle()); this.readersBook = bookCopy; } public MutableBook getBook() { // Do not return the book, but a new copy. Do not want the readers // book to change it's state if developer changes book after this call. MutableBook bookCopy = new MutableBook(); bookCopy.setTitle(this.readersBook.getTitle()); return bookCopy; } public int getPage() { // primitives are already immutable. return page; } /** * Must return reader instance since it's state has changed. **/ public ImmutableReader turnPage() { return new ImmutableReader(this.readersBook, page + 1); } } 

为了让你的课堂真正成为不可改变的,它必须符合以下要求:

  • 所有的class级成员都被宣布为最终
  • 在构造类时,所有在类级别中使用的variables都必须实例化。
  • 没有类variables可以有一个setter方法。
    • 这是从第一个陈述隐含的,但是要说清楚,你不能改变这个class级的状态。
  • 所有子对象也必须是不可变的,或者它们的状态在不可变类中永远不会改变。
    • 如果您有一个具有可变属性的类,则必须将其locking。 声明它是私人的,并确保你永远不会改变它的状态。

多学一点看看我的博客文章: http : //keaplogik.blogspot.com/2015/07/java-immutable-classes-simplified.html

一个简单的不可变类:

 public final class ImmutableStudent { private final int id; private final String name; public ImmutableStudent(int id, String name) { this.name = name; this.id = id; } public int getId() { return id; } public String getName() { return name; } } 

具有可变字段的不可变类:

 package com.programmer.gate.beans; public final class ImmutableStudent { private final int id; private final String name; private final Age age; public ImmutableStudent(int id, String name, Age age) { this.name = name; this.id = id; Age cloneAge = new Age(); cloneAge.setDay(age.getDay()); cloneAge.setMonth(age.getMonth()); cloneAge.setYear(age.getYear()); this.age = cloneAge; } public int getId() { return id; } public String getName() { return name; } public Age getAge() { Age cloneAge = new Age(); cloneAge.setDay(this.age.getDay()); cloneAge.setMonth(this.age.getMonth()); cloneAge.setYear(this.age.getYear()); return cloneAge; } } 

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