定义
享元模式(Flyweight Pattern)主要用于减少创建对象的数量,以减少内存占用和提高性能。这种类型的设计模式属于结构型模式,它提供了减少对象数量从而改善应用所需的对象结构的方式。
适用场景:
- 应用于系统底层开发。以解决系统性能问题
- 系统有大量相似对象,需要缓冲池场景
具体场景
享元模式主要包含三种角色:
- 抽象享元角色(IFlyweight):享元对象抽象基类或者接口,同时定义出对象的外部状态和内部状态的接口或实现。
- 具体享元角色:实现抽象享元角色,该角色的内部状态处理应该与环境无关,不会出现一个操作改变内部状态,同时改变外部状态的情况。
- 享元工厂:负责管理享元对象池和创建享元对象。
以英雄联盟兵线为例:有四种小兵,前排兵,后排兵,炮车,超级兵,每个兵种都有自己的颜色(忽略因时间变化等级升高的影响和大龙buff加持的影响等等),可以用享元模式简单实现一下前排兵,后排兵的创建(跑车和超级兵省略)。
小兵接口
public interface Minion {
String getType();
String getColour();
default void attack(){
System.out.println(getColour()+"色方的"+getType()+"正在攻击....");
}
}
近战兵:
//近战兵
public class MeleeMinion implements Minion{
private String colour;
MeleeMinion(String colour) {
this.colour = colour;
}
@Override
public String getType() {
return "近战兵";
}
@Override
public String getColour() {
return colour;
}
}
远程兵:
//远程兵
public class CasterMinion implements Minion{
private String colour;
CasterMinion(String colour) {
this.colour = colour;
}
@Override
public String getType() {
return "远程兵";
}
@Override
public String getColour() {
return this.colour;
}
}
享元工厂:
class MinionFactory {
private static Map<String, Minion> minionPool = new ConcurrentHashMap<>();
//因为内部状态的不变性,所以缓存起来作为主键
static Minion getMinion(String type, String colour){
Minion minion;
if("远程兵".equals(type)){
if(!minionPool.containsKey(colour)){
minion = new CasterMinion(colour);
minionPool.put(colour,minion);
}
}else if("近战兵".equals(type)){
if(!minionPool.containsKey(colour)){
minion = new MeleeMinion(colour);
minionPool.put(colour,minion);
}
}
minion = minionPool.get(colour);
return minion;
}
}
测试:
public class Test {
public static void main(String[] args) {
Minion minion = MinionFactory.getMinion("近战兵", "蓝");
minion.attack();
Minion minion2 = MinionFactory.getMinion("近战兵", "蓝");
minion2.attack();
System.out.println(minion == minion2);
Minion minion3 = MinionFactory.getMinion("远程兵", "红");
minion3.attack();
}
}
uml类图:
Integer(Long)中的享元模式
先测试以下代码:
public static void main(String[] args) {
Integer a = 100;
Integer b = 100;
System.out.println(a==b);
Integer c = 200;
Integer d = 200;
System.out.println(c==d);
}
一个为true,一个为false???是不是意想不到。
打上断点,调试:
可以得出,a和b是同一个对象,c和d不是同一个对象,进入Integer源代码,我们可以注意到这样一段代码:
/**
* Returns an {@code Integer} instance representing the specified
* {@code int} value. If a new {@code Integer} instance is not
* required, this method should generally be used in preference to
* the constructor {@link #Integer(int)}, as this method is likely
* to yield significantly better space and time performance by
* caching frequently requested values.
*
* This method will always cache values in the range -128 to 127,
* inclusive, and may cache other values outside of this range.
*
* @param i an {@code int} value.
* @return an {@code Integer} instance representing {@code i}.
* @since 1.5
*/
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
Integer的valueOf方法:当值大于等于low或者小于等于high时会调用IntegerCache.cache()方法,否则直接new一个新的Integer,我们继续看IntegerCache的源码:
实际上,jdk中的Long类也有这样的处理。
IntegerCache内部类上有这样的注释
/**
* Cache to support the object identity semantics of autoboxing for values between
* -128 and 127 (inclusive) as required by JLS.
*
* The cache is initialized on first usage. The size of the cache
* may be controlled by the {@code -XX:AutoBoxCacheMax=<size>} option.
* During VM initialization, java.lang.Integer.IntegerCache.high property
* may be set and saved in the private system properties in the
* sun.misc.VM class.
*/
大致意思我们可以通过设置AutoBoxCacheMax这个参数来控制包装类的最大缓存大小。
我们设置上参数,再运行
-XX:AutoBoxCacheMax=300