标签:
不可变对象永远不会发生改变,其字段的值只在构造函数运行时设置一次,其后就不会再改变。例如JDK中常见的两种基本数据类型String和Integer,它们都是不可变对象。为了理解immutable与mutable的区别,可以看看下面的一段代码:
package date0804.demo2; import java.awt.Point; public class ImmutableString { public static void main(String[] args) { //String,immutable String str = new String("new book"); System.out.println(str); str.replaceAll("new", "old"); System.out.println(str); //Point,mutable Point point = new Point(0,0); System.out.println(point); point.setLocation(1, 0); System.out.println(point); } }
new book new book java.awt.Point[x=0,y=0] java.awt.Point[x=1,y=0]
[拓展,下面一段代码与immutable无关]再看另外一个有趣的String实例:
package date0804.demo2; public class ImmutableString { public static void main(String[] args){ String str=new String("xyz"); change(str); System.out.println(str); } public static void change(String s) { s="xml"; } }
xyz
在并发编程中,一种被普遍认可的原则就是:尽可能的使用不可变对象来创建简单、可靠的代码。不可变对象特别有用,由于创建后不能被修改,所以不会出现由于线程干扰产生的错误或是内存一致性错误。使用不可变对象降低了垃圾回收所产生的额外开销,也减少了用来确保使用可变对象不出现并发错误的一些额外代码。
不可变对象可以在很大程度上简化我们的程序代码,并且具有以下优点:
——不可变对象易于构造,使用和测试;
——自动地为线程安全型,避免了一些繁杂的同步问题;
——不需要一个复制构造函数(copy constructor);
——不需要一个clone的实现;
——允许hashCode()方法的懒汉模式实现,并且缓存它的返回值;
——当作为一个字段时,不需要复制;
——一旦构造以后,具有类型不变性,不用检查;
——[引用名言]if an immutable object throws an exception, it‘s never left in an undesirable or indeterminate state。
——确保类不会被覆写,即该类不会被继承,实现这一点要加上修饰符final;或者使用静态工厂创建方法,确保构造函数私有的;
——所有的字段必须是私有的,并且加上修饰符final;
——所有的设置只需简单的一个构造函数即可,不要使用空构造函数,不要使用Javabean风格的构造函数;
——不要提供任何可以改变对象的方法,不仅仅是setter,一切能够修改对象状态的方法都要避免;
——如果该类有可变字段的对象,则必须进行保守型复制,并且只能由该类自身进行修改。
final public class ImmutableRGB { // 常量字段 final private int red; final private int green; final private int blue; final private String name; //私有方法,检查三基色的值是否在0~255之间,若不符合,则抛出异常 private void check(int red, int green, int blue) { if (red < 0 || red > 255 || green < 0 || green > 255 || blue < 0 || blue > 255) { throw new IllegalArgumentException(); } } //唯一的一个构造方法,先检查字段是否合法,然后传递所有字段 public ImmutableRGB(int red, int green, int blue, String name) { check(red, green, blue); this.red = red; this.green = green; this.blue = blue; this.name = name; } //get方法 public int getRGB() { return ((red << 16) | (green << 8) | blue); } //get方法 public String getName() { return name; } //对颜色值进行求逆 public ImmutableRGB invert() { return new ImmutableRGB(255 - red, 255 - green, 255 - blue, "Inverse of " + name); } }
从上面一段代码可以看出,这是一个不可变的类,其对象是一个不可变对象。对象一旦创建,就不会被修改。最后的一段代码,反映了一旦对对象进行修改,不要返回对象本身,而是拷贝一份返回。这种不可变对象很好地解决了并发编程中的竞争问题,不用考虑线程的同步,因为对象一旦创建,不会改变。
下面一段代码显示了表面上看起来是一个immutable类,实际上则是一个mutable类:
import java.util.Date; public final class BrokenPerson { private String firstName; private String lastName; private Date dob; public BrokenPerson( String firstName, String lastName, Date dob) { this.firstName = firstName; this.lastName = lastName; this.dob = dob; } public String getFirstName() { return this.firstName; } public String getLastName() { return this.lastName; } public Date getDOB() { return this.dob; } }
Date myDate = new Date(); BrokenPerson myPerson = new BrokenPerson( "David", "O‘Meara", myDate ); System.out.println( myPerson.getDOB() ); myDate.setMonth( myDate.getMonth() + 1 ); System.out.println( myPerson.getDOB() );
Mon Mar 24 21:34:16 GMT+08:00 2013 Thu Apr 24 21:34:16 GMT+08:00 2013
import java.util.Date; public final class BetterPerson { private String firstName; private String lastName; private Date dob; public BetterPerson( String firstName, String lastName, Date dob) { this.firstName = firstName; this.lastName = lastName; this.dob = new Date( dob.getTime() ); } //....... public Date getDOB() { return new Date( this.dob.getTime() ); }
注意,对于所有mutable的字段,应该要使用拷贝来在类中"进进出出",这样才比较安全。
本文简单介绍了immutable的优点及其设计方法,不可变对象最大的缺点就是创建对象的开销,因为每一步操作都会产生一个新的对象,然而合理地使用immutable可以带来极大的好处。不可变类最适合表示抽象数据类型(如数字、枚举类型或颜色)的值。Java 类库中的基本数据类型的包装类(如Integer 、 Long 和 Float )都是不可变的,其它数字类型(如 BigInteger 和 BigDecimal )也是不可变的。表示复数或任意精度的有理数的类将比较适合设计为不可变类。甚至包含许多离散值的抽象类型(如向量或矩阵)也很适合设计成不可变类,这取决于你的应用程序。
另一个适合用不可变类实现的好示例就是事件 。事件的生命期较短,而且常常会在创建它们的线程之外的线程中消耗,所以使它们成为不可变的是利大于弊。大多数 AWT 事件类都没有严格的作为不可变类来实现。同样地,在通信系统的组件间进行消息传递,将消息对象设计成不可变的是明智的。
标签:
原文地址:http://my.oschina.net/zzw922cn/blog/487610