什么是缓存实例的不可变类?
该不可变类内部有一块区域,该区域可用来存储创建出来的实例。在使用该类型创建变量的时候,如果新创建的变量的内容和已存在实例的内容一样,那么直接返回已存在的实例而不会再创建新的对象。这样可以减少系统对于创建对象的开销,但是会浪费相应的存储空间用来缓存已存在的实例对象。
运行速度和存储空间二者有点此消彼长的意思,一般想要提高速度就会耗费更多的空间,想要节省空间也有可能降低运行速度。主要是根据实际需要来进行选择。
对于不可变类的实例来说,数值重复的对象是没有什么意义的,毕竟不可变类的实例永远不能被改变。也就是说两个数值一样的不可变类的对象将从使用上看永远一样。这就好比收集卡片的时候,两张完全一样的卡片对于收集是没有什么用的。所以,通常情况下,不可变类的实例都会建立相应的缓存区用来缓存已存在的实例。
如何创建一个缓存实例的不可变类?
首先,该类本身是一个不可变类。
其次,用private修饰构造器,使得无法在外界创建对象。同时提供一个专门用来创建对象的类方法。通过该方法创建对象的时候,会对对象进行判断,看该和该对象值一样的对象是否已经存在,已存在则返回已有的,不存在再创建。因为缓存实例的不可变类的关键一步就是新创建的对象要和缓存区中的对象进行比较。如果构造器为public修饰将会破坏这一规则,即外界可以通过构造器随意的创建对象。
再次,在该类内部定义一个缓存区,缓存区需要一个表示大小的变量,还需要一个指出已缓存元素的变量。因为创建对象的方法需要定义成一个类方法,而类方法中必然会调用有关缓存区的信息,所以缓存区及其相关信息都要用static修饰。(static修饰的成员无法直接访问非static修饰的成员)
以下为接下来代码中的关于缓存区案例的说明。代码取自疯狂Java讲义第三版中。
缓存区的大小为10,数据结构为一个数组。
由于数组一但创建其长度将永远固定不变,所以,当缓存区满的时候采用的处理原则为最新的内容覆盖最开始的内容
public class CacheImmutale
{
private final String name;//对于该类,只有一个有意义的成员变量name
private CacheImmutale(String name){
this.name = name;
}//隐藏构造器,使得外界无法通过构造器来创建对象
//定义一个getter方法
public name getName(){
return this.name;
}
//接下来的内容都是有关缓存区的定义
//定义缓存区的大小
private static int MAX_SIZE = 10;//缓存区容量为10
//定义一个变量用来表示缓存区中存储元素的个数
private static int pos = 0;//第二个作用可以表示接下来将要存储元素的位置
//创建缓存区
private static CacheImmutale[] cache = new CacheImmutale[MAX_SIZE];
//定义一个用来创建对象的实例方法
public static CacheImmutale valueOf(String name){
//先检查想要创建的对象是否已经存在,如果已存在则返回已存在的。
//此处主要是用了给对象的成员变量只有name,而name本身有事String类型
//所以利用了String类型的equals方法来进行的比较
for(int i = 0; i < MAX_SIZE; i++){
if (cache[i] != null && cache[i].getName().equals(name)){
return cache[i];
}
}
//注意此处利用的判断循环的条件为i <MAX_SIZE;即和整个缓存区中的内容进行比较。
//但是此时缓存区中的内容有可能还没有满,所以存在cache[i] == null的情况,所以
//if语句中才会写if(cache[i] == null && cache[i].getName().equals(name))
//通过上步检查可知想要创建的对象不存在,所以需要重新创建。
//创建新的变量分两种情况,第一种为缓存区已满,第二种为缓存区未满
if (pos == MAX_SIZE)//判断缓存区是否已满
{
pos = 0;
cache[pos++] = new CacheImmutale(name);
/*
等价代码
cache[0] = new CacheImmutale(name);
pos = 1;
两种方式均可
*/
}else{
cache[pos++] = new CacheImmutale(name);
}
return cache[pos - 1];//因为之前上述if else块中都有pos++,
//所以pos指向的是将要存入的新元素的位置,pos-1才是当前元素的位置。所以
//返回的是cache[pos - 1];
}
//重写equals()方法
public boolean equals(Object obj){
if (this == obj){
return true;
}
if(obj != null && obj.getClass() == CacheImmutale.class){
CacheImmutale temp = (CacheImmutale)obj;
if (this.getName().equals(temp.getName())){
return true;
}//这段代码依据对象成员变量的特性——仅有一个String类型的成员变量
//可以简写成
//return this.name.equals(temp.getName());
}
return false;
}
//重写hashCode方法
public int hashCode(){
return this.name.hashCode() * 33;
}
}