• 享元模式(FlyWeight Pattern)及其在java自动拆箱、自动装箱中的运用


    本文主要从三个方面着手,第一:简要介绍享元模式。第二:享元模式在基本类型封装类中的运用以Integer为例进行阐述。第三:根据第一、第二的介绍,进而推出java是如何实现自动拆箱与装箱的。

    第一:简要介绍享元模式。

    享元模式的角色:抽象享元类(FlyWeight)、具体享元类(ConcreteFlyWeight)、享元工厂类(FlyWeightFactory)、客户类(Client,相当于一个测试类)

    至于FlyWeight pattern如果直接翻译应译为: 蝇量级模式。从蝇量级我们可以知道共享的对象是非常小的。在Integer中共享的数字范围为[-128,127]。老外是不是从共享的对象太小的角度,所以称它为FlyWeight Pattern呢?。

    我国人是从FlyWeight Pattern的用途(FlyWeight Pattern 的用途就是共享对象。元,也就是最小的意思。享元就是共享最小对象。)上进行翻译的。

    下面给出各个角色的代码:

    FlyWeight的代码如下:

    package com.qls.flyWeight2;
    
    public interface FlyWeight {
    	//以外蕴状态【external state】为参数的商业方法。这里假设外蕴状态是String类型的。
    	void operation(String externalState);
    }
    

     ConcreteFlyWeight的代码如下:

     1 package com.qls.flyWeight2;
     2 
     3 public class ConcreteFlyWeight implements FlyWeight {
     4     //内蕴状态的私有字段[internal state]
     5     private Character internalState;
     6     //构造一个以内蕴状态为参数的构造方法
     7     public ConcreteFlyWeight(Character internalState) {
     8         this.internalState = internalState;
     9     }
    10     @Override
    11     public void operation(String externalState) {
    12         // TODO Auto-generated method stub
    13         System.out.println("internalState="+internalState+" externalState="+externalState);
    14     }
    15 
    16 }

    FlyWeightFactory的代码如下:

     1 package com.qls.flyWeight2;
     2 
     3 import java.util.HashMap;
     4 import java.util.Map;
     5 import java.util.Map.Entry;
     6 
     7 public class FlyWeightFactory {
     8     Map<Character,FlyWeight> files=new HashMap<Character,FlyWeight>();
     9     //构造方法
    10     public FlyWeightFactory() {
    11         // TODO Auto-generated constructor stub
    12     }
    13     //写一个以内蕴状态的工厂方法,以生产FlyWeight对象。
    14     public FlyWeight factory(Character internalState){
    15         if(files.containsKey(internalState)){
    16             return files.get(internalState);
    17         }else{
    18             FlyWeight flyWeight = new ConcreteFlyWeight(internalState);
    19             //把对象保存到Map集合中。
    20             files.put(internalState, flyWeight);
    21             return flyWeight;
    22         }
    23     }
    24     public void checkFlyWeight(){
    25         //打印出FlyWeight对象,以及享元对象。
    26                 for(Entry<Character, FlyWeight> en:files.entrySet()){
    27                     System.out.println(en.getKey()+"="+en.getValue());
    28                 }
    29     }
    30 }

    Client的代码如下:

     1 package com.qls.flyWeight2;
     2 
     3 public class Client {
     4 
     5     public static void main(String[] args) {
     6         // TODO Auto-generated method stub
     7         //创建一个享元工厂对象
     8         FlyWeightFactory flyWeightFactory = new FlyWeightFactory();
     9         //向享元工厂对象请求一个内蕴状态为'a'的享元对象
    10         FlyWeight factory = flyWeightFactory.factory(new Character('a'));
    11         //以参量方式传入一个外蕴状态。
    12         factory.operation("ouyangfeng");
    13         //向享元工厂对象请求一个内蕴状态为'b'的享元对象
    14         FlyWeight factory2 = flyWeightFactory.factory(new Character('b'));
    15         //以参量方式传入一个外蕴状态。
    16         factory.operation("sixi");
    17         //向享元工厂对象请求一个内蕴状态为'a'的享元对象
    18         FlyWeight factory3 = flyWeightFactory.factory(new Character('a'));
    19         //以参量方式传入一个外蕴状态。
    20         factory.operation("wallow");
    21         System.out.println(factory==factory3);//返回结果为true。由此可以确定'a'确实做到了享元(共享元数据)
    22         /**
    23          * 打印出享元对象。以及内蕴状态。目的是证明对相同的内蕴状态只创建一个对象。
    24          * 下面语句的输出结果是:
    25          * b=com.qls.flyWeight2.ConcreteFlyWeight@21780f30
    26             a=com.qls.flyWeight2.ConcreteFlyWeight@512d297a
    27             这个输出结果证明了:factory==factory3.
    28          */
    29         flyWeightFactory.checkFlyWeight();
    30     }
    31 
    32 }

    第二:享元模式在基本类型封装类中的运用以Integer为例进行阐述。

    首先概述一下:在Integer类中享元对象是保存在一个IntegerCache.cache[]这个数组里面的这个数组存放的内容是:[-128,127]之间的数字,这个范围中的Integer,对象只创建一次。

    所以你要测验:Integer a=-128; Integer b=-128 ; System.out.println(a==b);输出结果为true. 而Integer c=-129; Integer b=-129 ; System.out.println(a==b);输出结果为false.

    要想说清楚这个问题只需看一看private static class IntegerCache 、 public static Integer valueOf(int i)、 public int intValue() 这三个类的源码即可。

    private static class IntegerCache的源码如下:(注:在这个源码中我添加了,对这个源码的一些解释)

     1  private static class IntegerCache {
     2         static final int low = -128;
     3         static final int high;
     4         static final Integer cache[];
     5 //静态代码块保证了,Integer cache[]在IntegerCache加载时就会被赋值。赋值的结果见第22行
     6         static {
     7             // high value may be configured by property
     8             int h = 127;
     9             String integerCacheHighPropValue =
    10                 sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
    11             if (integerCacheHighPropValue != null) {
    12                 int i = parseInt(integerCacheHighPropValue);
    13                 i = Math.max(i, 127);
    14                 // Maximum array size is Integer.MAX_VALUE
    15                 h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
    16             }
    17             high = h;
    18 
    19             cache = new Integer[(high - low) + 1];
    20             int j = low;
    21             for(int k = 0; k < cache.length; k++)
    22                 cache[k] = new Integer(j++);//这个循环语句等价于Integer[] cache={new Integer(-128),new Integer(-127),new Integer(-126).........new Integer(126),new Integer(127)}共127+128+1=256个数字。
    23         }//当我们在Integer a=f;直接从这个cache[]数组中进行搜索如果f在[-128,127]之间(例如Integer a=1)不需要new Integer(1)了,而是在这个cache[]数组中等于1的值直接赋值。反之如果 f不在[-128,127]之间则会new Integer(f);这个源代码见下面的valueOf
    24 //即可豁然开朗。同时这也体现了享元模式的运用。在享元模式示例中用HashMap存放对象,对同一个对象只创建一次。这里用的是数组存放对象,对同一个对象也只创建一次。
    25         private IntegerCache() {}
    26     }
    valueOf的源代码如下:
    1  public static Integer valueOf(int i) {
    2         assert IntegerCache.high >= 127;
    3         if (i >= IntegerCache.low && i <= IntegerCache.high)
    4             return IntegerCache.cache[i + (-IntegerCache.low)];
    5         return new Integer(i);
    6     }
    public static Integer valueOf(int i)这个方法用于把int转换为Integer,它是编译器用于自动装箱的核心方法。由编译器根据代码的实际情况,决定是否调用这个方法。一句话在把int变为Integer时,编译器会自动调用这个方法。
    intValue()的源代码如下:
    1  public int intValue() {
    2         return value;
    3     }
    public int intValue()这个方法用于把Integer转换为int的。
    ,它是编译器用于自动拆箱的核心方法。由编译器根据代码的实际情况,决定是否调用这个方法。一句话在把Integer变为int时,编译器会自动调用这个方法。
    下面这个例子说明在一下三种情况编译器是如何工作的:三种情况是:int 与Integer    Integer与Integer    int 与int
     1 package com.qls.gateway;
     2 
     3 public class Test6 {
     4 
     5     public static void main(String[] args) {
     6         // TODO Auto-generated method stub
     7         int g=1;
     8         int a=1;
     9         int f=189;
    10         //b与c在[-128,127]这个范围内。
    11         Integer b=1;
    12         Integer c=1;
    13         //d和e不在[-128,-127]这个范围内。
    14         Integer d=189;
    15         Integer e=189;
    16         System.out.println(a==g);//true【这个是int 与int的比较】
    17         /**
    18          * 从这两个输出语句System.out.println(f==d);
    19         System.out.println(a==b);得出一个结论当int 与Integer比较时,编译器会把Integer自动拆箱。
    20         证明如下:假设当int 与Integer比较时,编译器会把int自动装箱。那么int f=189.自动装箱过后变为
    21         new Integer(189)相当于用创建了一个对象所以与Integer d=189。不可能是同一个对象,那么f==d也就为
    22         false了,但是输出结果为true。所以假设不成立。
    23         所以当int 与Integer比较时,编译器会把Integer自动拆箱。
    24          */
    25         System.out.println(f==d);
    26         System.out.println(a==b);
    27         /**
    28          * Integer与Integer比较时:只需看Integer的范围是否在[-128,127]如果在直接从cache[]数组中取,
    29          * 则必然是同一个对象,反之如果不在,则会创建一个对象。
    30          */
    31         System.out.println(d==e);//输出false
    32         System.out.println(b==c);//输出true.
    33     }
    34 
    35 }
    
    
    
     
  • 相关阅读:
    Linux直接在通过终端打开图片文件
    【暑假】[实用数据结构]UVa11995 I Can Guess the Data Structure!
    【暑假】[实用数据结构]动态范围查询问题
    【暑假】[实用数据结构]范围最小值问题(RMQ)
    【暑假】[实用数据结构]动态连续和查询问题
    【暑假】[基本数据结构]基本的数据结构知识点总结梳理
    【暑假】[基本数据结构]根据in_order与post_order构树
    【暑假】[基本数据结构]根据BFS与DFS确定树
    【暑假】[网络流]网络流知识总结
    [HDOJ2546] 饭卡 (01背包)
  • 原文地址:https://www.cnblogs.com/1540340840qls/p/6201598.html
Copyright © 2020-2023  润新知