如何在Java中创build不可变的对象?
如何在Java中创build不可变的对象?
哪些对象应该被称为不可变的?
如果我有所有静态成员的类是不可改变的?
下面是一个不可变对象的硬性要求。
- 使课程最终
- 使所有成员最终,明确地设置,在一个静态块,或在构造函数
- 使所有成员保密
- 没有修改状态的方法
- 要非常小心地限制对可变成员的访问(记住该字段可能是
final
但对象仍然可以是可变的,即private final Date imStillMutable
)。 在这些情况下你应该制作defensive copies
。
上课final
的理由非常微妙,经常被忽视。 如果不是最后的人可以自由地扩展你的class级,重写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; } }
在实践中,在dependency injection环境中遇到上述问题是非常普遍的。 你没有明确地实例化事物,你给的超类的引用实际上可能是一个子类。
拿走是为了保证不变性,你必须把课程标记为final
。 在Joshua Bloch的Effective Java中深入介绍了这一点,并在Java内存模型的规范中明确引用。
类不是不可变的,对象是。
不可变的手段:我的公开可见状态在初始化后不能改变。
尽pipe它可以帮助确保线程的安全性,但并不需要声明域名是最终的
如果你的类只有静态成员,那么这个类的对象是不可变的,因为你不能改变对象的状态(你可能不能创build它:))
只是不要将公共的mutator(setter)方法添加到类中。
要在Java中创build一个不可变的类,可以记住以下几点:
1.不要提供setter方法来修改任何类的实例variables的值。
2.宣布课程为“最终” 。 这将阻止任何其他类扩展它,从而覆盖任何可能修改实例variables值的方法。
3.声明实例variables为private和final 。
4.您也可以将该类的构造函数声明为private,并在需要时添加一个工厂方法来创build该类的实例。
这些点应该帮助!
要创build一个不可变的类,您需要遵循以下步骤:
Declare the class as final so it can't be extended. Make all fields private so that direct access is not allowed. Don't provide setter methods for variables Make all mutable fields final so that it's value can be assigned only once. Initialize all the fields via a constructor performing deep copy. Perform cloning of objects in the getter methods to return a copy rather than returning the actual object reference.
首先,你知道为什么你需要创build不可变对象,以及不可变对象的优点是什么。
不可变对象的优点
并发性和multithreading它自动线程安全,所以同步问题….等
不需要复制构造函数不需要执行克隆。 类不能被重写使该字段作为私有的和最终的 Force调用者在一个步骤中完全构造一个对象,而不是使用无参数构造函数
不可变对象只是对象,其状态意味着对象的数据在构build不可变对象后不能改变。
请看下面的代码。
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(); } }
从oracle站点,如何在Java中创build不可变的对象。
- 不要提供“setter”方法 – 修改字段引用的字段或对象的方法。
- 使所有的领域最终和私人。
- 不要让子类重写方法。 最简单的方法是将类声明为final。 更复杂的方法是使构造函数保持私有状态,并在工厂方法中构造实例。
- 如果实例字段包含对可变对象的引用,则不允许更改这些对象:
I.不要提供修改可变对象的方法。
II。 不要共享对可变对象的引用。 切勿将引用存储到传递给构造函数的外部可变对象; 如有必要,创build副本,并存储对副本的引用。 同样,必要时创build内部可变对象的副本,以避免在方法中返回原件。
最小化可变性
一个不可变的类只是一个其实例不能被修改的类。 包含在每个实例中的所有信息在创build时提供,并在对象的生命周期中被固定。
JDK不可变类:string,盒装原始类(包装类),BigInteger和BigDecimal等
如何使一个类不可变?
- 不要提供任何修改对象状态的方法(称为mutators)。
- 确保课程无法扩展。
- 使所有的领域最终。
- 使所有的字段保密。 这可以防止客户端获得对字段引用的可变对象的访问权限并直接修改这些对象。
-
做防御性的副本。 确保独占访问任何可变组件。
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()); } }
欲了解更多信息,请参阅我的博客:
http://javaexplorer03.blogspot.in/2015/07/minimize-mutability.html
如果一个对象的状态一旦创build就无法更改,那么这个对象被称为不可变的 在Java中创build不可变类的最简单的方法之一是将所有的字段设置为final。如果需要编写包含可变类(如java.util.Date)的不可变类。 为了在这种情况下保持不变性,build议返回原始对象的副本,
不可变对象是那些一旦创build就不能改变其状态的对象,例如String类是一个不可变的类。 不可变对象不能被修改,所以它们在并发执行中也是线程安全的。
不可变类的特征:
- 简单构build
- 自动线程安全
- Map键和Set作为内部状态的好候选人在处理时不会改变
- 因为它们总是表示相同的状态,所以不需要实现克隆
编写不可变类的钥匙:
- 确保课程不能被重写
- 使所有成员variables私人和最终
- 不要给他们的setter方法
- 在施工阶段不应该泄漏对象引用
- 不要提供“setter”方法 – 修改字段引用的字段或对象的方法。
- 使所有的领域最终和私人。
- 不要让子类重写方法。 最简单的方法是将类声明为final。 更复杂的方法是使构造函数保持私有状态,并在工厂方法中构造实例。
- 如果实例字段包含对可变对象的引用,则不允许更改这些对象:
- 不要提供修改可变对象的方法。
- 不要共享对可变对象的引用。 切勿将引用存储到传递给构造函数的外部可变对象; 如有必要,创build副本,并存储对副本的引用。 同样,必要时创build内部可变对象的副本,以避免在方法中返回原件。
当你想把任何类作为一个不可变类时,必须考虑以下几个步骤。
- class级应该被标记为最终的
- 所有的领域必须是私人的和最终的
- 用构造函数replacesetter(用于赋值给variables)。
让我们看一下上面input的内容:
//ImmutableClass package younus.attari; public final class ImmutableExample { private final String name; private final String address; public ImmutableExample(String name,String address){ this.name=name; this.address=address; } public String getName() { return name; } public String getAddress() { return address; } } //MainClass from where an ImmutableClass will be called package younus.attari; public class MainClass { public static void main(String[] args) { ImmutableExample example=new ImmutableExample("Muhammed", "Hyderabad"); System.out.println(example.getName()); } }