什么是不可变类
是指当创建了这个类的实例后,就不允许修改它的属性值。在JDK的基本类库中,所有基本类型的包装类,如Integer和Long类,都是不可变类,java.lang.String也是不可变类。
如何创建一个不可变类
1. 所有成员都是private
2. 不提供对成员的改变方法,例如:setXXXX
3. 确保所有的方法不会被重载。手段有两种:使用final Class(强不可变类),或者将所有类方法加上final(弱不可变类)。
4. 如果某一个类成员不是原始变量(primitive)或者不可变类,必须通过在成员初始化(in)或者get方法(out)时通过深度clone方法,来确保类的不可变。
(primitive变量: boolean,byte, char, double ,float, integer, long, short)。
下面是一个自定义不可变类的例子:
public final class FinalClass {
private final int[] myArray;
public FinalClass(int[] anArray) {
this.myArray = anArray.clone();
}
public String toString() {
StringBuffer sb = new StringBuffer("Numbers are: ");
for (int i = 0; i < myArray.length; i++) {
sb.append(myArray[i] + " ");
}
return sb.toString();
}
}
不可变类的用途
不可变类的实例的状态不会变化,这样的实例可以安全地被其他与之关联的对象共享,还可以安全地被多个线程共享。
为了节省内存空间,优化程序的性能,应该尽可能地重用不可变类的实例,避免重复创建具有相同属性值的不可变类的实例。
下面是一个缓存不可变类实例的例子:
public class CacheImmutable//定义了一个不可变类,使用这个类的数组实现
{
//数组来缓存已有的实例
private static CacheImmutable[] cache=new CacheImmutable[10];
//设置一个变量来指向数组的偏移量
private static int pos=0;
private final String name;
public CacheImmutable(String name)
{
this.name=name;
}
public String getName()
{
return this.name;
}
public static CacheImmutable valueOf(String name)
{ //遍历缓冲池
for(int i=0;i<10;i++)
{ //如果有相同的实例,直接返回已有实例
if(cache[i] !=null && cache[i].getName().equals(name))//这里有可能出现空指针错误,注意格式就行!!!!我调试很很久!!
{
return cache[i];
}
//如果已满,则就要把第一个覆盖
if(pos==10)
{ pos=0;
cache[pos++]=new CacheImmutable(name);
}
//否则就放在最后一个位置
else
{
cache[pos++]=new CacheImmutable(name);
}
}
return cache[pos-1];
}
//重写equals方法
public boolean equals(Object obj)
{
if(this ==obj)
{
return true;
}
if(obj!=null&&obj.getClass()==CacheImmutable.class)
{
CacheImmutable ci =(CacheImmutable)obj;
return obj.equals(ci.getName());
}
return false;
}
//重写hashCode方法
public int hashCode()
{
return name.hashCode();
}
}
public class CacheImmutableText
{
public static void main(String[] args) throws NullPointerException//为了避免出空指针错误,这里把错误抛出!!!!
{
CacheImmutable c1=CacheImmutable.valueOf("Hello");
CacheImmutable c2=CacheImmutable.valueOf("Hello");
System.out.println(c1==c2);
}
}
总结:
1. 为了防止耗尽内存,在实例缓存中存放的是对象的软引用(SoftReference),如果一个对象仅仅持有软引用,Java虚拟机会在内存不足的情况下回收它的内存
2. 在程序的生命周期中,对于程序不需要经常访问的实例,应该使用new语句创建它,使它能及时结束生命周期
3. 对于程序需要经常访问的实例,那就用valueOf()方法来获得它,因为该方法能把实例放到缓存中,使它可以被重用
4. 只有满足以下条件的不可变类才需要实例缓存
A、不可变类的实例的数量有限
B、在程序运行过程中,需要频繁访问不可变类的一些特定实例。这些实例拥有与程序本身同样长的生命周期