如何在Java中创建不可变对象?
哪些对象应被称为不可变的?
如果我有一个所有成员都是静态的类,它是不可变的吗?
如何在Java中创建不可变对象?
哪些对象应被称为不可变的?
如果我有一个所有成员都是静态的类,它是不可变的吗?
final
,但对象仍然可能是可变的。例如:private final Date imStillMutable
)。在这些情况下,应该进行防御性拷贝
。将类声明为final
的原因非常微妙,经常被忽视。如果它不是final,人们可以自由地扩展您的类,覆盖public
或protected
行为,添加可变属性,然后提供他们的子类作为替代品。通过声明类为final
,您可以确保这种情况不会发生。
为了看到问题的实际情况,请考虑以下示例:
public class MyApp{
/**
* @param args
*/
public static void main(String[] args){
System.out.println("Hello World!");
OhNoMutable mutable = new OhNoMutable(1, 2);
ImSoImmutable immutable = mutable;
/*
* Ahhhh Prints out 3 just like I always wanted
* and I can rely on this super immutable class
* never changing. So its thread safe and perfect
*/
System.out.println(immutable.add());
/* Some sneak programmer changes a mutable field on the subclass */
mutable.field3=4;
/*
* Ahhh let me just print my immutable
* reference again because I can trust it
* so much.
*
*/
System.out.println(immutable.add());
/* Why is this buggy piece of crap printing 7 and not 3
It couldn't have changed its IMMUTABLE!!!!
*/
}
}
/* This class adheres to all the principles of
* good immutable classes. All the members are private final
* the add() method doesn't modify any state. This class is
* just a thing of beauty. Its only missing one thing
* I didn't declare the class final. Let the chaos ensue
*/
public class ImSoImmutable{
private final int field1;
private final int field2;
public ImSoImmutable(int field1, int field2){
this.field1 = field1;
this.field2 = field2;
}
public int add(){
return field1+field2;
}
}
/*
This class is the problem. The problem is the
overridden method add(). Because it uses a mutable
member it means that I can't guarantee that all instances
of ImSoImmutable are actually immutable.
*/
public class OhNoMutable extends ImSoImmutable{
public int field3 = 0;
public OhNoMutable(int field1, int field2){
super(field1, field2);
}
public int add(){
return super.add()+field3;
}
}
类不是不可变的,对象才是。
不可变意味着:我的公共可见状态在初始化之后无法更改。
字段不一定要声明为final,尽管这可以极大地帮助确保线程安全性。
如果您的类只有静态成员,则该类的对象是不可变的,因为您无法更改该对象的状态(您可能也无法创建它 :))。
不要向类中添加公共 mutator(setter)方法。
- 不要提供“setter”方法——修改字段或字段所引用的对象的方法。
- 使所有字段都为final和private。
- 不允许子类重写方法。最简单的方法是将类声明为final。更复杂的方法是将构造函数设置为私有,并在工厂方法中构建实例。
- 如果实例字段包括对可变对象的引用,请勿允许更改这些对象:
I. 不提供修改可变对象的方法。
II. 不共享对可变对象的引用。永远不要存储传递给构造函数的外部可变对象的引用;如有必要,请创建副本,并存储对副本的引用。同样,在必要时创建内部可变对象的副本,以避免在方法中返回原始对象。
首先,您知道为什么需要创建不可变对象以及不可变对象的优点。
不可变对象的优点
并发和多线程 它自动支持线程安全,因此无需同步问题....等等
不需要复制构造函数 不需要实现克隆。 类不能被覆盖 将字段设置为private和final 强制调用者在单个步骤中完全构造对象,而不是使用无参数构造函数
不可变对象只是指其状态意味着对象数据在构建不可变对象后无法更改的对象。
请参见下面的代码。
public final class ImmutableReminder{
private final Date remindingDate;
public ImmutableReminder (Date remindingDate) {
if(remindingDate.getTime() < System.currentTimeMillis()){
throw new IllegalArgumentException("Can not set reminder" +
" for past time: " + remindingDate);
}
this.remindingDate = new Date(remindingDate.getTime());
}
public Date getRemindingDate() {
return (Date) remindingDate.clone();
}
}
最小化可变性
一个不可变类是指其实例无法被修改的类。每个实例中包含的所有信息都在创建时提供,并且在对象的生命周期内保持不变。
JDK中的不可变类有:String、装箱原始类型(wrapper classes)、BigInteger和BigDecimal等。
如何使一个类成为不可变类?
进行防御性复制。 确保对任何可变组件的独占访问权。
public List getList() { return Collections.unmodifiableList(list); <=== 在将其返回给调用者之前,对可变字段进行防御性复制 }
如果您的类具有任何引用可变对象的字段,请确保该类的客户端无法获取对这些对象的引用。永远不要将这样的字段初始化为客户端提供的对象引用或从访问器返回对象引用。
import java.util.Date;
public final class ImmutableClass {
public ImmutableClass(int id, String name, Date doj) {
this.id = id;
this.name = name;
this.doj = doj;
}
private final int id;
private final String name;
private final Date doj;
public int getId() {
return id;
}
public String getName() {
return name;
}
/**
* Date class is mutable so we need a little care here.
* We should not return the reference of original instance variable.
* Instead a new Date object, with content copied to it, should be returned.
* */
public Date getDoj() {
return new Date(doj.getTime()); // For mutable fields
}
}
import java.util.Date;
public class TestImmutable {
public static void main(String[] args) {
String name = "raj";
int id = 1;
Date doj = new Date();
ImmutableClass class1 = new ImmutableClass(id, name, doj);
ImmutableClass class2 = new ImmutableClass(id, name, doj);
// every time will get a new reference for same object. Modification in reference will not affect the immutability because it is temporary reference.
Date date = class1.getDoj();
date.setTime(date.getTime()+122435);
System.out.println(class1.getDoj()==class2.getDoj());
}
}