薯拾

flyweight-pattern

2019-07-07

享元模式

  • 动机

    软件系统采用纯粹对象方案的问题在于大量细粒度的对象会很快充斥在系统中,从而带来很高的运行时代价

  • 定义

    运用共享技术有效的支持大量细粒度对象。

  • 总结

    1. 面向对象很好的解决了抽象性的问题,需要考虑对象的代价问题,同时不触及面向对象的抽象性问题。
    2. Flyweight采用对象共享的做法来降低系统中独享的个数,从而降低细粒度对象给系统带来的内存压力。
    3. 对象共享的前提是对象是只读的。

Java对象池

  1. Long 缓存池

    Long 这个类并没有照搬享元模式,Long 内部维护了一个静态的对象池,仅缓存了 [-128,127] 之间的数字,这个对象池在 JVM 启动的时候就创建好了,而且这个对象池一直都不会变化,也就是说它是静态的。之所以采用这样的设计,是因为 Long 这个对象的状态共有 264种,实在太多,不宜全部缓存,而 [-128,127] 之间的数字利用率最高。下面的示例代码出自 Java 1.8,valueOf() 方法就用到了 LongCache 这个缓存.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    Long valueOf(long l) {
    final int offset = 128;
    // [-128,127]直接的数字做了缓存
    if (l >= -128 && l <= 127) {
    return LongCache
    .cache[(int)l + offset];
    }
    return new Long(l);
    }
    //缓存,等价于对象池
    //仅缓存[-128,127]直接的数字
    static class LongCache {
    static final Long cache[]
    = new Long[-(-128) + 127 + 1];
    static {
    for(int i=0; i<cache.length; i++)
    cache[i] = new Long(i-128);
    }
    }
  2. String 缓存池

    • 使用双引号创建字符串,先检查String pool 中是否有相同的值;
    • 使用new创建时,强制从堆中新建一个对象;
    • 可以使用 intern()将一个string对象手动存储到pool;
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      private static void StringTest(){
      String s1 = "a";
      String s2 = "b";
      String s3 = "ab";
      String s4 = "ab";
      System.out.println("s3==s4? " + (s3 == s4)); // true
      // 编译期优化成"ab", 可以从生成的class文件反编译得到证实
      String s5 = "a" + "b";
      System.out.println("s3==s5? " + (s3 == s5)); //true
      // 无法进行编译期优化
      String s6 = s1 + s2;
      System.out.println("s3==s6? " + (s3 == s6)); //false
      // 强制在堆新建
      String s7 = new String("ab");
      System.out.println("s3==s7? " + (s3 == s7)); //false
      // final 表示可以进行编译期优化 "ab"
      final String s8 = "a";
      final String s9 = "b";
      String s10 = s8 + s9;
      System.out.println("s3==s10? " + (s3 == s10));//true
      }
      生成的class文件反编译的结果:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      private static void StringTest() {
      String s1 = "a";
      String s2 = "b";
      String s3 = "ab";
      String s4 = "ab";
      System.out.println("s3==s4? " + (s3 == s4));
      String s5 = "ab";
      System.out.println("s3==s5? " + (s3 == s5));
      System.out.println("erere" + (s1 + s2 == s3));
      String s6 = s1 + s2;
      System.out.println("s3==s6? " + (s3 == s6));
      String s7 = new String("ab");
      System.out.println("s3==s7? " + (s3 == s7));
      String s8 = "a";
      String s9 = "b";
      String s10 = "ab";
      System.out.println("s3==s10? " + (s3 == s10));
      }
  3. Integer

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    private static  void IntTest(){
    // 缓存池范围内(-128~127),重用对象
    Integer i1 = 100;
    Integer i2 = 100;
    System.out.println("i1==i2 "+(i1==i2)); // true
    // 超过缓存池
    Integer i3 = 128;
    Integer i4 = 128;
    System.out.println("i3==i4? "+(i3==i4)); // false
    // 堆内新建
    Integer i5 = new Integer(100);
    Integer i6 = new Integer(100);
    System.out.println("i5==i6? "+(i5==i6)); // false
    int i7 = 128;
    int i8 = 128;
    System.out.println("i7==i8? "+(i7==i8)); // true
    }
使用支付宝打赏
使用微信打赏

若你觉得我的文章对你有帮助,欢迎点击上方按钮对我打赏

扫描二维码,分享此文章