不可变类的例子
我已经知道不可变类的定义,但是我需要一些例子。
标准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(但是这有一些可变的静态状态 –
out
,err
) - java.lang.Math(这也是随机数生成器)
- java.lang.reflect.Array中
- java.util.Collections中
- java.util.Arrays中
不可修改的类在构build后不能更改。 所以,例如,一个Java String
是不可变的。
为了使一个类不可变,你必须把它作为final
,所有的字段是private
和final
。 例如,下面的类是不可变的:
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一个不可变的类,您需要遵循以下步骤:
- 将这个类声明为final,所以不能扩展。
- 将所有字段设为私有,以便不允许直接访问。
- 不要为variables提供setter方法
- 使所有可变字段最终,以便它的值只能分配一次。
- 通过执行深度复制的构造函数初始化所有的字段。
- 在getter方法中执行克隆对象以返回副本而不是返回实际的对象引用。
一个例子可以在这里find。
我们也可以使用Builder Pattern轻松创build不可变的类,可以在这里find一个例子。
Sun(Oracle)文档有一个关于如何创build一个不可变对象的极好的清单。
- 不要提供“setter”方法 – 修改字段引用的字段或对象的方法。
- 使所有的领域最终和私人。
- 不要让子类重写方法。 最简单的方法是将类声明为final。 更复杂的方法是使构造函数保持私有状态,并在工厂方法中构造实例。
- 如果实例字段包含对可变对象的引用,则不允许更改这些对象:
- 不要提供修改可变对象的方法。
- 不要共享对可变对象的引用。 切勿将引用存储到传递给构造函数的外部可变对象; 如有必要,创build副本,并存储对副本的引用。 同样,必要时创build内部可变对象的副本,以避免在方法中返回原件。
来自: http : //download.oracle.com/javase/tutorial/essential/concurrency/imstrat.html
该网站还提供了在并发环境中使用的示例,但在编写库时,不变性也很有用。 它确保来电者能够只改变我们允许的内容。
String
是不可变类的一个很好的“现实世界”例子。 你可以将它与可变的StringBuilder
类进行对比。
大部分用于reflection的Java类都是不可变的。 还有一些是“几乎不可变的”:例如,实现Accessible
的类只有一个setAccessible
方法,用于更改Accessible
实例的状态。
我相信标准类库中还有很多。
LocalDate , LocalTime和LocalDateTime类(从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一个不可变的类