结合JDK源码看设计模式——享元模式

前言

  在说享元模式之前,你一定见到过这样的面试题

public class Test {

    public static void main(String[] args) {
Integer a=Integer.valueOf(127);
Integer b=new Integer(127);
System.out.println(a==b);
int c=127;
System.out.println(a==c);
System.out.println(b==c);
}
}

  问你输出结果是什么?有些人可能一下就看出了答案是什么,有些人可能不是特别清楚。那么一起看下面的文章。我想你很快就能知晓。

一、享元模式定义

  提供了减少对象数量从而改善应用所需的对象结构方式,使用共享技术有效地支持大量细粒度的对象。

二、适用场景

  常用于系统底层开发,以便解决系统的性能问题。像数据库连接池,里面都是创建好的连接对象,在这些连接对象中有我们需要的则直接拿来用,避免重新创建。如果没有我们需要的,则创建一个。

  在一个系统中有大量相似对象,需要缓冲池的场景。不需要一直创建一个新的对象,可以直接从缓冲池里拿。这样可以降低系统内存,同时提高效率。

三、内部状态与外部状态

  内部状态:

    在享元模式内部,不随外界的改变而改变。比如说Integer类中的MIN_VALUE和MAX_VALUE两个值,无论外部传什么值。都不会改变这两个值。结合JDK源码看设计模式——享元模式

  外部状态:

    这就很好理解了,就是随外部的改变而改变的状态。比如说Integer类中的value值。

结合JDK源码看设计模式——享元模式 四、Integer中的享元模式

  经过上面的介绍,你肯定大概了解了享元模式的概念。那么我们来看看Integer中的享元模式具体是怎么样的吧。

  

public final class Integer extends Number implements Comparable<Integer> {
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
private final int value;
public Integer(int value) {
this.value = value;
}
}

  上面是我简化了的Integer类。平常在使用Integer类的时候。你是否思考过用valueOf还是用new创建Integer对象。看完源码就会发现在valueOf这个方法中它会先判断传进去的值是否在IntegerCache中,如果不在就创建新的对象,在就直接返回缓存池里的对象。这个valueOf方法就用到享元模式。它将-128到127的Integer对象先在缓存池里创建好,等我们需要的时候直接返回即可。所以在-128到127中的数值我们用valueOf创建会比new更快。如果你还想继续往里钻研,可以去看看IntegerCache如何实现。在这里我们主要说设计模式。看接下来的一个测试代码及内存分析。

结合JDK源码看设计模式——享元模式

  这里就可以很清晰的看出来a和b的内存不相等。结果当然是false。回到我们的设计模式上来,在实际的场景中我们更多的是完成缓冲池的创建,来达到缓冲池对象里面复用的功能。就像下面这种情况,尽管我定义了两个不同的对象,但实际上我指向的是同一块内存地址,这样就减少了系统内存,并且使系统的响应速度更快。

结合JDK源码看设计模式——享元模式

五、总结

  在享元模式里我们要理解享元,“享”就表示共享,“元”表示对象。当我们频繁需要这个对象的时候,我们考虑new,考虑clone等等这些方法。当然这些方法实际上用的场景和这个不一样。看上面的内存分析就能知道,我们频繁需要相同的一个范围内的对象去做某件事情,我们还需要重新创建对象就会有两个缺点:第一就是内存浪费,第二就是性能稍慢,特别是我这个对象new起来需要响应的时间很长的时候。这时候考虑用享元模式来先创建一个缓冲池会更好。这个缓冲池可以放在容器中进行存储,当我们需要的时候直接拿出来用即可。一次创建,多次使用。

  在这里多说一点就是当int类型和Integer比较的时候会自动的拆箱也就是只比较里面的值大小是否相等,所以上面的答案就是false,true,true。

上一篇:Myexclipse创建Junit测试


下一篇:linux 初始设置