Hardcore Java 4 —— Immutable

来源:互联网 发布:nginx 加载配置文件 编辑:程序博客网 时间:2024/06/06 06:54

immutable 和上一篇中final有很大的关系,immutable对我们实例化类产生了很大的影响。

在java中,遇到一个很大的问题就是在构造函数中,很多变量使用的其实总是引用。这些引用是在传值,这就有个漏洞,我们实例化后的对象可能会被改变,如果某个部分改变了被引用地址的值,那么我们实例化的object也被改变了。

如果程序的一个部分将这个值置为null,而object中,这个值不能为null,那么这个时候,程序就会以 NullPointerExceptions结束。

所以,解决问题的办法就是,在实例化之后,object就不能被改变,我们就称这样的type为immutable types。

虽然,immutable objects在很多语言上都存在,但在java中,能正确的使用immutable types 是guru的表现。

——————————————————————————————————————————————————————————

1 Fundamentals

最基本的immutable class 就是没有write methods。

An immutable person
package oreilly.hcj.immutable;
 
 
 
public class ImmutablePerson {
 
  private String firstName;
 
  private String lastName;
 
  private int age;
 
 
 
  public ImmutablePerson(final String firstName, final String lastName, 
 
                         final int age) {
 
    if (firstName == null) {
 
      throw new NullPointerException("firstName"); 
 
    }
 
    if (lastName == null) {
 
      throw new NullPointerException("lastName"); 
 
    }
 
    this.age = age;
 
    this.firstName = firstName;
 
    this.lastName = lastName;
 
  }
 
 
 
  public int getAge( ) {
 
    return age;
 
  }
 
 
 
  public String getFirstName( ) {
 
    return firstName;
 
  }
 
 
 
  public String getLastName( ) {
 
    return lastName;
 
  }
 
}

Cracked Immutables

Simmons说这样的定义的immutable还是不行。高手可以使用反射改变内部变量。这里第一个来救火的是 final,这样,反射也没法了。

package oreilly.hcj.immutable;
 
 
 
public class ImmutablePerson {
 
  private final String firstName;
 
  private final String lastName;
 
  private final int age;
 
 
 
  public ImmutablePerson(final String firstName, final String lastName, 
 
                         final int age) {
 
    if (firstName == null) {
 
      throw new NullPointerException("firstName"); 
 
    }
 
    if (lastName == null) {
 
      throw new NullPointerException("lastName"); 
 
    }
 
    this.age = age;
 
    this.firstName = firstName;
 
    this.lastName = lastName;
 
  }
 
 
 
  public int getAge( ) {
 
    return age;
 
  }
 
 
 
  public String getFirstName( ) {
 
    return firstName;
 
  }
 
 
 
  public String getLastName( ) {
 
    return lastName;
 
  }
 
}

3 False Immutable Types

但是,仅仅是这样还是有问题。当我们传入对象的时候,因为在Set 中遗留的对象句柄,还是成为漏洞。例如:

package oreilly.hcj.immutable;
 
 
 
import java.awt.Point;
 
 
 
public class SomeClass {
 
  
 
  public final static void main(final String[] args) {
 
    Point position = new Point(25, 3);
 
    SomeData data = new SomeData(position);
 
    position.x = 22;
 
    System.out.println(data.getValue( ));
 
  }
 
}

在其他类中使用:

package oreilly.hcj.immutable;
 
 
 
import java.awt.Point;
 
 
 
public class SomeData {
 
 
 
  private final Point value;
 
 
 
  public SomeData (final Point value) {
 
    this.value = value;
 
  }
 
 
 
  public Point getValue( ) {
 
    return value;
 
  }
 
}

试着运行一下:

>ant -Dexample=oreilly.hcj.immutable.SomeClass run_examplerun_example:     [java] java.awt.Point[x=22,y=3]

所以,本以为不可改变的类,其实很脆。

second fire caller 闪亮登场——new 。

这里的策略是:copy。

package oreilly.hcj.immutable;
 
 
 
import java.awt.Point;
 
 
 
public class SomeBetterData {
 
  /** Holds the value */
 
  private final Point value;
 
 
 
  public SomeBetterData(final Point value) {
 
    this.value = new Point(value);
 
  }
 
 
 
  public Point getValue( ) {
 
    return new Point(value);
 
  }
 
}

———————————————————————————————————————————— 

Whenever you are creating immutable types with parameters that are not immutable, make sure that you copy all of the mutable parameters. However, if the parameters are immutable themselves, you can afford to simply store the reference to the object; since neither the class nor the caller can change the value, the object is safe.

————————————————————————————————————————————————————

 

Immutable Problems

      使用immutable带来最大的问题就是开销。immutable just eat memmory as candy. String is a good example.

这是我们很多场合下最好是使用String Buffer 的原因。但是也要避免在使用String Buffer的时候,使用“+”等操作符。因为这样Complier就会另外使用String,简单的问题就会复杂化。

所以,下面代码的功能是一样的,但是,性能会差很多。

不好代码:

 final StringBuffer buf = new StringBuffer(500);  buf.append("{");  for (int idx = 0; idx < array.length; idx++) {    buf.append("[" + idx + "] " + array[idx]);  }  buf.append("}");  return buf.toString( );
好的代码:
  final StringBuffer buf = new StringBuffer(500);  buf.append("{");  for (int idx = 0; idx < array.length; idx++) {    buf.append("[");    buf.append(idx);    buf.append("] ");    buf.append(array[idx]);  }  buf.append("}");  return buf.toString( );

.csharpcode, .csharpcode pre{font-size: small;color: black;font-family: consolas, "Courier New", courier, monospace;background-color: #ffffff;/*white-space: pre;*/}.csharpcode pre { margin: 0em; }.csharpcode .rem { color: #008000; }.csharpcode .kwrd { color: #0000ff; }.csharpcode .str { color: #006080; }.csharpcode .op { color: #0000c0; }.csharpcode .preproc { color: #cc6633; }.csharpcode .asp { background-color: #ffff00; }.csharpcode .html { color: #800000; }.csharpcode .attr { color: #ff0000; }.csharpcode .alt {background-color: #f4f4f4;width: 100%;margin: 0em;}.csharpcode .lnum { color: #606060; }

二者的差距是巨大的:

>ant oreilly.hcj.review.BufferingBadPerformance run_example
 
run_example:
 
     [java] Building 10000 element Fibbonacci Number array took 0 millis
 
     [java] Using dumpArray took 150 millis
 
     [java] Using dumpArrayBetter took 40 millis
 
     [java] Using dumpArrayReallyBad took 88347 millis

.csharpcode, .csharpcode pre{font-size: small;color: black;font-family: consolas, "Courier New", courier, monospace;background-color: #ffffff;/*white-space: pre;*/}.csharpcode pre { margin: 0em; }.csharpcode .rem { color: #008000; }.csharpcode .kwrd { color: #0000ff; }.csharpcode .str { color: #006080; }.csharpcode .op { color: #0000c0; }.csharpcode .preproc { color: #cc6633; }.csharpcode .asp { background-color: #ffff00; }.csharpcode .html { color: #800000; }.csharpcode .attr { color: #ff0000; }.csharpcode .alt {background-color: #f4f4f4;width: 100%;margin: 0em;}.csharpcode .lnum { color: #606060; }_____________________________________________________________________________________________________________

Immutable or Not

There are good reasons to make things immutable, but there are also good reasons to make them mutable. Although you should make each decision on a case-by-case basis, the following factors should help:

  • If the object is supposed to be a constant, it should always be immutable.

  • immutable ——如果object被设计成常量。

  • If the object will be changed frequently, it should be a mutable object. For example, a class such as StringBuffer is changed frequently, so it wouldn't make much sense as an immutable object.

mutable ——如果object需要经常的改变。

  • If the object is very large, be careful if you opt for immutability. Large immutable objects need to be copied in order to be changed; this copying can slow down a program significantly.

  • mutable——如果object很大。

  • Sets and other collections returned from a method should be immutable to preserve encapsulation.

原创粉丝点击