1.基本数据类型的自动拆箱与装箱
??什么是自动装箱拆箱
基本数据类型的自动装箱(autoboxing)、拆箱(unboxing)是自J2SE 5.0开始提供的功能。
一般我们要创建一个类的对象的时候,我们会这样:
Class a = new Class(parameter);
当我们创建一个Integer对象时,却可以这样:
Integer i = 100; (注意:不是 int i = 100; )
实际上,执行上面那句代码的时候,系统为我们执行了:Integer i = new Integer(100); 此即基本数据类型的自动装箱功能。
??基本数据类型与对象的差别
基本数据类型不是对象,也就是使用int、double、boolean等定义的变量、常量。
基本数据类型没有可调用的方法。
eg: int t = 1; t. 后面是没有方法滴。
Integer t =1; t. 后面就有很多方法可让你调用了。
??什么时候自动装箱
例如:Integer i = 100;
相当于编译器自动为您作以下的语法编译:Integer i = new Integer(100);
??什么时候自动拆箱
自动拆箱(unboxing),也就是将对象中的基本数据从对象中自动取出。如下可实现自动拆箱:
1Integer i= 10;//装箱
2 int t= i;//拆箱
在进行运算时,也可以进行自动装箱与拆箱。
1Integer i= 10;
2System.out.println(i++);
??Integer的自动装箱
//在-128~127 之外的数
Integer i1= 200;
Integer i2= 200;
System.out.println("i1==i2:"+(i1==i2));
// 在-128~127 之内的数
Integer i3= 100;
Integer i4= 100;
System.out.println("i3==i4:"+(i3==i4));
输出的结果是:
i1==i2:false
i3==i4:true
说明:
equals() 比较的是两个对象的值(内容)是否相同。
"==" 比较的是两个对象的引用(内存地址)是否相同,也用来比较两个基本数据类型的变量值是否相等。
在自动装箱时对于值从–128到127之间的值,它们被装箱为Integer对象后,会存在内存中被重用,
所以范例中,i3 与 i4实际上参考至同一个对象。
如果超过了从–128到127之间的值,被装箱后的Integer对象并不会被重用,
即相当于每次装箱时都新建一个 Integer对象,所以范例中,i1与i2参考的是不同的对象。
另外,当不使用自动装箱功能的时候,情况与普通类对象一样,请看下例:
1Integer i3= new Integer(100);
2Integer i4= new Integer(100);
3System.out.println("i3==i4:"+(i3==i4));//显示false
??String 的拆箱装箱
先看个例子:
1 String str1= "abc";
2 String str2= "abc";
3 System.out.println(str2==str1);//输出为 true
4 System.out.println(str2.equals(str1));//输出为 true
5
6 String str3= new String("abc");
7 String str4= new String("abc");
8 System.out.println(str3==str4);//输出为 false
9 System.out.println(str3.equals(str4));//输出为 true
这个怎么解释呢?貌似看不出什么。那再看个例子。
1 String d="2";
2 String e= "23";
3 e= e.substring(0,1);
4 System.out.println(e.equals(d));//输出为 true
5 System.out.println(e==d);//输出为 false
第二个例子中,e的初始值与d并不同,因此e与d是各自创建了个对象,(e==d)为false 。
同理可知,第一个例子中的str3与str4也是各自new了个对象,而str1与str2却是引用了同一个对象。
2.享元模式:
本篇我们将会讲述结构性模式中的另外一个非常有用的模式-享元模式,享元模式的特点是,复用我们内存中已存在的对象,降低系统创建对象实例的性能消耗。在.NET下的值类型和引用类型的内存分配机制,我这里就不做详细的讲解了,包括引用类型与值类型之间的装箱和拆箱的操作。我们来给出个简单的享元模式的应用前后的对比图,大概我们就知道享元模式的重要作用了。
我们这里以绘制一个有样式的字体来说明吧,有的时候我们想绘制一个纯色的文字,比如红色,那么我们可能需要创建很多的实例,通常来说,这些实例的差别不大,这个时候,我们可以考虑复用其中创建的某个实例,而不用去new这么多相同的对象,来完成这样的工作。我们下面以这个例子来说明,使用享元模式的前后对比的情况。
使用享元模式前:
使用享元模式后:
4.1、享元模式的特点
享元模式的意图是通过共享有效支持大量细粒度的对象,来提供应用程序的性能,节省系统中重复创建对象实例的性能消耗,这个怎么理解呢?其实就是以下几点的含义:
1、当我们系统中某个对象类型的实例较多的情况。
2、并且要求这些实例进行分类后,发现真正有区别的分类很少的情况。
例如我们的生活中很多的场景,我们在使用拼音输入的法的时候,如果说我们每个字都是new一个对象实例的操作的话,那么内存中的实例就太可
怕,这个时候,我们是不是可以考虑将这些重复的字体在内存中只是创建一次,而是通过复用对象的形式,来组织一些可能有多个字符重复的内容呢?
也许这是一个不错的主意,我们来看看这个示例的过程吧。
4.2、享元模式的使用场景
1、当我们发现某个类型的对象有大量的实例时,我们是否可以对这些实例进行分类,经过分类后,我们发现只有很少的类别的情况下。
2、我们发现通过使用享元模式后能够提高系统的性能和不会带来更多的复杂度时。
享元模式一般是给出本地内存资源节省的一个方案,并不适合互联网上的分布式应用的情况,不过享元模式对于排他性的要求资源的控制,是个不
错的选择的。
五、享元模式的经典实现
我们下面来根据上面的我们对输入法中的字体来进行分析,给出相关的示例代码:
字体类型的基类:
public class FontBase
{
private List<string> font = new List<string>();
private string fontName;
public FontBase(string name)
{
this.fontName = name;
}
public FontBase AddFont(string font)
{
this.font.Add(font);
return this;
}
public virtual string FontName
{
get
{
return this.fontName;
}
}
}
具体的文字类型类:
public class ChineseFont : FontBase
{
public ChineseFont()
: base("ChineseFont")
{
base.AddFont("ChineseFont");
}
}
public class EnglishFont : FontBase
{
public EnglishFont()
: base("EnglishFont")
{
base.AddFont("EnglishFont");
}
}
具体的创建工厂类:
public class FontFactory
{
private Dictionary<string, FontBase> fonts = new Dictionary<string, FontBase>();
public FontBase Create(string name)
{
FontBase fontBase = fonts[name];
if (fontBase != null)
return fontBase;
fontBase = (FontBase)Activator.CreateInstance(Type.GetType(name));
return fontBase;
}
}
通过上面实例的讲解我们知道,我们通过缓存对象类型的形式来控制对象实例的创建过程,经典的模式中没有体现共享的状态,比如说我们在外部可能对于
享元对象来说是不共享的,内部是共享的。下面我们来看看享元模式的变种吧。
六、享元模式的其他方案
对于上面的经典方案带来的问题,可能我们需要更好的考虑,我们如何应对多种对象类型,我们如何管理并共享这些对象实例,这些都是我们需要考虑的问
题,经过上面的思考,我们这里可以参考我们平时开发的ORM中的连接池的思路,我们这里对享元模式提供-对象池的技术。
我们在配置文件中控制对象池中的某个类型对象实例的数量,对象的生命周期的时间,默认初始化多少个对象实例,以提前准备,为后续的使用提供服务。
我们这里可以设计出这个专门的对象池,我们可以提供如下的功能:
1、根据对象类型动态的创建对象实例。
2、根据对象池中的配置,在对象池中找到空闲的实体提供给程序调用,减少创建对象的次数。
3、我们需要设计每个类型的缓冲池,通过把对象进行缓存,提供性能。如果对象池中的对象长期不会调用,那么我们会提供一个销毁对象的机制。
我们来看看对象池的设计结构吧:
通过上面的几个组件,来协调完成对象池的工作。
这里给出相关的示例代码:
我们先给出配置文件配置缓冲池的配置信息:
<?xml version="1.0" encoding="utf-8" ?> | |||||||||||||||||
02 | <Cache> | ||||||||||||||||
03 | <ObjectSection name="" type=""/> | ||||||||||||||||
04 | <ObjectCache> | ||||||||||||||||
05 | <object name="" type="" max="" timeout="" /> | ||||||||||||||||
06 | <object name="" type="" max="" timeout="" /> | ||||||||||||||||
07 | <object name="" type="" max="" timeout="" /> | ||||||||||||||||
08 | <object name="" type="" max="" timeout="" /> | ||||||||||||||||
09 | <object name="" type="" max="" timeout="" /> | ||||||||||||||||
10 | <object name="" type="" max="" timeout="" /> | ||||||||||||||||
11 | <object name="" type="" max="" timeout="" /> | ||||||||||||||||
12 | <object name="" type="" max="" timeout="" /> | ||||||||||||||||
13 | <object name="" type="" max="" timeout="" /> | ||||||||||||||||
14 | </ObjectCache> | ||||||||||||||||
15 | </Cache> | ||||||||||||||||
16 |
| ||||||||||||||||
17 | 我们来看看具体的工厂类: | ||||||||||||||||
1 | <div class="cnblogs_Highlighter"><pre class="brush:html"> public class ObjectFactory | ||||||||||||||||
2 | { | ||||||||||||||||
3 | public static T Build<T>() where T:class,new() | ||||||||||||||||
4 | { | ||||||||||||||||
5 | return new T(); | ||||||||||||||||
6 | } | ||||||||||||||||
7 | } | ||||||||||||||||
8 | </pre> | ||||||||||||||||
9 | </div> | ||||||||||||||||
1 | 我们来看看具体的缓存类: |
七、享元模式使用总结
享元模式的主旨意在通过共享对象节省总体资源,不过共享对象将导致资源访问上的一些问题,包括对象实例的销毁,还包括线程间的共享和线程内的共享是不同的,我们知道,一般线程退出的时候,会自动释放线程内部的申请的资源,.NET会自动通过GC来回收,但是对于线程间共享的对象也许不会自动回收,这些内容需要宿主进程来进行回收,当然可能这些我们也可以通过对象池来完成这样的回收机制。 或者说也可以参考操作系统中的
队列的情况,通过回调函数来通知进行对象的回收等。我们在对于项目中需要大量实例对象的创建工作的时候,我们就考虑一下是不是需要享元模式的应用了,希望大家能在项目中找到合适的切入点,使用合适的模式来提高程序的适应性和健壮性。由于本人水平有限,不足或者错误之处,还请大家批
评指出。