不可变类
- 不可变类的意思是创建该类实例后,该实例的 - 实例变量是不可改变的。- Java中的8个包装类和java.lang.String类都是不可变类,当创建它们的实例后,其实例变量不可改变。
 
- Java中的
- 如果需要创建自定义的不可变类,遵循以下规则: - 使用 - private和- final修饰符来修饰该类的- 成员变量
- 提供 - 带参数构造器,用于- 根据传入参数来初始化类中的成员变量
- 仅为该类的成员变量 - 提供getter方法,- 不要为该类的成员变量提供setter方法,因为普通方法无法修改final修饰的成员变量
- 如果有必要,重写hashCode()和equals()。 - equals()方法根据关键成员变量来作为两个对象是否相等的标准
- 应保证两个用equals()方法判断为相等的对象的hashCode()也相等
 - 例如String类对象里的 - 字符序列作为相等的标准,其- hashCode()方法也是根据- 字符序列计算得到的。- String类中的 - equals()方法源码- 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21- public boolean equals(Object anObject) { 
 if (this == anObject) {
 return true;
 }
 if (anObject instanceof String) {
 String anotherString = (String)anObject;
 int n = value.length;
 if (n == anotherString.value.length) {
 char v1[] = value;
 char v2[] = anotherString.value;
 int i = 0;
 while (n-- != 0) {
 if (v1[i] != v2[i])
 return false;
 i++;
 }
 return true;
 }
 }
 return false;
 }- String类中的hashCode()方法的源码: - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12- public int hashCode() { 
 int h = hash;
 if (h == 0 && value.length > 0) {
 char val[] = value;
 
 for (int i = 0; i < value.length; i++) {
 h = 31 * h + val[i];
 }
 hash = h;
 }
 return h;
 }
 
- 可变类的含义是该类的- 实例变量是- 可变的。大部分创建的类都是可变类,特别是JavaBean,因为总是为其实例变量提供了setter和getter方法。
- 与可变类相比, - 不可变类的实例在整个生命周期中永远处于初始化状态,它的实例变量不可改变。因此对不可变类的实例的控制将更加简单。
- 如果需要设计一个不可变类,尤其要注意其 - 引用类型的成员变量,如果- 引用类型的成员变量的类是可变的,就必须采取必要的措施来- 保护该成员变量所引用的对象不会被修改,这样才能创建真正的不可变类。
缓存不可变类
- 不可变类的实例状态不可改变,可以很 - 方便的被多个对象共享。如果程序经常使用相同的不可变实例,就应该考虑- 缓存这种- 不可变类的实例。毕竟- 重复创建相同的对象没有意义,而且会加大系统开销。
- 用 - 数组创建缓存池,用于缓存实例:- 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74- class CacheImmutale 
 {
 private static int MAX_SIZE = 10;
 // 使用数组来缓存已有的实例
 private static CacheImmutale[] cache
 = new CacheImmutale[MAX_SIZE];
 // 记录缓存实例在缓存中的位置,cache[pos-1]是最新缓存的实例
 private static int pos = 0;
 private final String name;
 private CacheImmutale(String name)
 {
 this.name = name;
 }
 public String getName()
 {
 return name;
 }
 public static CacheImmutale valueOf(String name)
 {
 // 遍历已缓存的对象,
 for (int i = 0 ; i < MAX_SIZE; i++)
 {
 // 如果已有相同实例,直接返回该缓存的实例
 if (cache[i] != null
 && cache[i].getName().equals(name))
 {
 return cache[i];
 }
 }
 // 如果缓存池已满
 if (pos == MAX_SIZE)
 {
 //先进先出
 // 把缓存的第一个对象覆盖,即把刚刚生成的对象放在缓存池的最开始位置。
 cache[0] = new CacheImmutale(name);
 // 把pos设为1
 pos = 1;
 }
 else
 {
 // 把新创建的对象缓存起来,pos加1
 cache[pos++] = new CacheImmutale(name);
 }
 return cache[pos - 1];
 }
 public boolean equals(Object obj)
 {
 if(this == obj)
 {
 return true;
 }
 if (obj != null && obj.getClass() == CacheImmutale.class)
 {
 CacheImmutale ci = (CacheImmutale)obj;
 return name.equals(ci.getName());
 }
 return false;
 }
 public int hashCode()
 {
 return name.hashCode();
 }
 }
 public class CacheImmutaleTest
 {
 public static void main(String[] args)
 {
 CacheImmutale c1 = CacheImmutale.valueOf("hello");
 CacheImmutale c2 = CacheImmutale.valueOf("hello");
 // 下面代码将输出true
 System.out.println(c1 == c2);
 }
 }
- 是否需要隐藏缓存池类的构造器完全取决于系统需求。盲目乱用缓存也可能导致系统性能下降,缓存的对象会占用系统内存,如果某个对象只使用一次,重复使用的概率不大,缓存该实例就弊大于利;反之,如果某个对象需要频繁地重复使用,缓存该实例就利大于弊。 
- Java中 - Integer类就采取了上述CacheImmutale类相同的处理策略,如果采用- new构造器来创建Integetr对象,则每次返回- 全新的Integer对象;如果采用- valueOf()方法来创建Integer对象,则会- 缓存该方法创建的对象。
- 由于new构造器方式创建Integer对象不会启用缓存,因此性能较差,所以 - Java9中已经将该构造器标记为过时,全面采用valueOf()方法创建。- 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19- public class IntegerCacheTest 
 {
 public static void main(String[] args)
 {
 // 生成新的Integer对象
 Integer in1 = new Integer(6);
 // 生成新的Integer对象,并缓存该对象
 Integer in2 = Integer.valueOf(6);
 // 直接从缓存中取出Ineger对象
 Integer in3 = Integer.valueOf(6);
 System.out.println(in1 == in2); // 输出false
 System.out.println(in2 == in3); // 输出true
 // 由于Integer只缓存-128~127之间的值,
 // 因此200对应的Integer对象没有被缓存。
 Integer in4 = Integer.valueOf(200);
 Integer in5 = Integer.valueOf(200);
 System.out.println(in4 == in5); //输出false
 }
 }- 由于Integer只缓存-128~127之间的Integer对象,因此两次通过Integer.valueOf(200)方法生成的Integer对象不是同一个。
 
- 由于Integer只缓存-128~127之间的Integer对象,因此两次通过