不可变类的示例

use*_*041 47 java immutability

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

Paŭ*_*ann 90

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

  • java.lang.String(已经提到)
  • 原始类型的包装类: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(用于构建异常堆栈跟踪)
  • 大多数枚举类都是不可变的,但实际上这取决于具体情况.(不要实现可变的枚举,这会让你搞砸了.)我认为至少标准API中的所有枚举类实际上都是不可变的.

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

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

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

  • java.awt.BasicStroke - 在图形上下文中绘制线条的辅助对象
  • java.awt.Color - (至少这个类的对象,一些子类可能是可变的或取决于一些外部因素(如系统颜色)),以及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和子类.
  • 所有类别的java.time除外DateTimeException都是不可变的.大多数子类的类java.time也是不可变的.

可以说原始类型也是不可变的 - 你不能改变42的值,是吗?


Class AccessControlContext是一个不可变的类

AccessControlContext没有任何变异方法.它的状态包含一个ProtectionDomains列表(它是一个不可变类)和一个DomainCombiner.DomainCombiner是一个接口,因此原则上实现可以在每次调用时执行不同的操作.

实际上,ProtectionDomain的行为也可能取决于当前有效的策略 - 是否将这样的对象称为不可变是有争议的.

和AccessController?

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

这对于所有其他没有对象(实例)的类都有效,最着名的是:

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

  • "你不能改变42的价值,可以吗?"......你不能主要因为它是答案:) (3认同)

chr*_*ann 22

构建后不能更改不可变类.因此,例如,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);
     }
}
Run Code Online (Sandbox Code Playgroud)

我在代码示例中添加了一个方法,展示了如何处理集合,这一点非常重要.

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

  • 如果保留引用,你仍然可以在`Person`构造之后更改`friends``Collection`.它应该克隆`朋友`.有了三个参数,你应该开始考虑使用[builder](http://www.informit.com/articles/article.aspx?p=1216151&seqNum=2)=) (8认同)

小智 13

重要的是要记住,将一个类声明为final并不意味着它是"不可变的",这基本上意味着这个类不能被扩展(或专门化).

不可变类必须具有私有和最终字段(没有setter),因此在构造之后,它的字段值不能更改.


Pan*_*kaj 9

要创建一个不可变类,您需要按照以下步骤操作:

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

这里可以找到一个例子.

我们也可以使用Builder Pattern轻松创建不可变类,这里可以找到一个例子.


Igo*_*tch 7

LocalDate,LocalTimeLocalDateTime类(从1.8开始)也是不可变的.事实上,这个主题是在OCAJSE8(1Z0-808)考试中,这正是我决定将其视为仅仅是评论的原因.

所有原始包装类(如Boolean,Character,Byte,Short,Integer,Long,FloatDouble)都是不可变的.

MoneyCurrency API(定义为Java9)也应该是不可变的.

顺便提一下,阵列支持的Lists(由其创建Arrays.asList(myArray))在结构上是可更改的.

此外,还有一些边界线情况,例如java.util.Optional(在OCP考试有特色,1Z0-809),如果包含的元素本身是不可变的,它是不可变的.


Ste*_*n C 5

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


大多数用于反射的 Java 类都是不可变的。其他一些是“几乎不可变的”:例如,实现的类Accessible只有一个setAccessible改变实例状态的方法Accessible


我确信标准类库中还有更多内容。