对象在计算机中是虚拟的东西,它的“重”和“轻”并非指实际重量,而它们“所使用的内存大小”。使用内存多的对象就是“重”的对象,使内存少的对象就是“轻”的对象。
为了能够在计算机中保存对象,需要分配给其足够的内存空间。当程序中需要大量对象时,如果都使用new关键字来分配内存,将会消耗大量内存空间。
关于Flyweight模式,一言以蔽之就是“通过尽量共享实例来避免new出实例”。当需要某个实例时,并不总通过new关键字来生成实例,而是尽量共用已经存在的实例。这就是Flyweight的核心。
示例程序
在示例程序中,有一个将许多普通字符组合成为“大型字符”的类,它的实例就是重实例。为了进行测试,我们以文件的形式保存了大型字符’0’-‘9’和’-'字体数据。
示例程序类图
BigChar类
该类是表示“大型字符”的类,它会从文件中读取大型字符的字体数据,并将它们保存在内存中,然后使用print方法输出大型字符,大型字符会消耗很多内存,因此我们需要考虑如何共享BigChar类的实例。
package FlyWeight;
import java.io.BufferedReader;
import java.io.FileReader;
public class BigChar {
private char charname;
private String fontdata;
public BigChar(char charname) {
// TODO Auto-generated constructor stub
this.charname = charname;
try {
BufferedReader bufferedReader = new BufferedReader(new FileReader("big"+charname+".txt"));
String line;
StringBuffer stringBuffer = new StringBuffer();
while((line = bufferedReader.readLine())!=null){
stringBuffer.append(line);
stringBuffer.append("\n");
}
bufferedReader.close();
this.fontdata = stringBuffer.toString();
} catch (Exception e) {
// TODO: handle exception
this.fontdata = charname + "?";
}
}
public void print(){
System.out.print(fontdata);
}
}
BigCharFactory类
该类会根据需要生成BigChar类的实例。不过如果它发现之前已经生成某个大型字符的BigChar类的实例,则会直接利用该实例,而不会再生成新的实例。生成的实例全部被保存自pool字段中。此外,为了能够快速查找出之前是否已经生成了某个大型字符所对应的实例,我使用了java.util.Hashmap类。
package FlyWeight;
import java.util.HashMap;
public class BigCharFactory {
private HashMap pool = new HashMap();
private static BigCharFactory singlton = new BigCharFactory();
private BigCharFactory(){}
public static BigCharFactory getInstance(){
return singlton;
}
public synchronized BigChar getBigChar(char charname){
BigChar bcBigChar = (BigChar)pool.get(""+charname);
if(bcBigChar==null){
bcBigChar = new BigChar(charname);
pool.put(""+charname, bcBigChar);
}
return bcBigChar;
}
}
这里使用了单例模式。
BigString类
BigString类用于将多个BigChar组成“大型字符串”。
package FlyWeight;
public class BigString {
private BigChar[] bigChars;
public BigString(String string) {
// TODO Auto-generated constructor stub
bigChars = new BigChar[string.length()];
BigCharFactory bigCharFactory = BigCharFactory.getInstance();
for(int i=0;i<bigChars.length;i++){
bigChars[i] = bigCharFactory.getBigChar(string.charAt(i));
}
}
public void print(){
for(int i=0;i<bigChars.length;i++){
bigChars[i].print();
}
}
}
Main类
package FlyWeight;
public class Main {
public static void main(String[] args) {
// TODO Auto-generated method stub
if(args.length==0){
System.out.println("Usage:java Main digits");
System.out.println("Example:java Main 1212123");
System.exit(0);
}
BigString bigString = new BigString(args[0]);
bigString.print();
}
}
带参数运行
Java中的垃圾回收机制
在java程序中可以通过new关键字分配内存空间。如果分配了过多的内存,就会导致内存不足,这时,运行Java程序的虚拟机就会开始垃圾回收处理,它会查看自己的内存空间(堆空间)是否存在没有被使用过的实例,如果存在就释放该实例,这样就可以回收可用的内存空间。此处的关键是垃圾回收器会“释放没有被使用过的实例”。垃圾回收器在进行垃圾回收的过程中,会判断实例是否是垃圾。如果其他对象引用了该实例,垃圾回收器就会认为“该实例正在被使用”,不会将其当作垃圾处理掉。
虽然我们不能显示地删除实例,但我们可以删除对实例的引用,要想让实例可以被垃圾回收器回收掉,只需要显示地将其置于管理对象外即可。