您的位置:首页 > 其它

设计模式-单例模式学习笔记及心得感悟

2014-12-26 18:00 519 查看

单例模式

单例模式的定义

确保某一个类只有一个实例,并且自行实例化并向整个系统提供这个实例。

通用代码如下

//饿汉单例模式[code]publicclassSingleton{
privatestaticfinalSingletonsingleton=newSingleton();
//限制产生多个对象
privateSingleton(){}
//通过该方法获得实例对象
publicstaticSingletongetSingleton(){
returnsingle;
}
//类中其他的方法,尽量是static
}


单例模式的优点

1.在内存中只有一个实例,减少了内存的开支,特别是一个对象需要频繁的创建、销毁时,而且创建或者销毁时性能又无法优化,此时其优势就很明显。

2.减少了系统性能开销。当一个对象的产生需要消耗较多的资源时,可以通过在启动应用时直接产生一个单例对象,用永久驻留内存的方式来解决。

3.避免对资源的多重占用,避免对一个资源同时进行多种冲突的操作。

4.单例模式可以在系统设置全局的访问点,优化和共享资源访问。例如cocos2dx中的导演类CCDirector,网上查到的这个类的职能是:

它负责管理初始化OpenGL渲染窗口以及游戏场景的流程控制,它是cocos2dx游戏开发中必不可少的类之一。为什么要把此类设计成单例对象呢?因为,一个游戏只需要有一个游戏窗口就够了,所以,只需要初始化一次OpenGL渲染窗口。而且场景的流程控制功能,也只需要存在一个这样的场景控制对象即可。为了保证CCDirector类只存在一个实例对象,就必须使用单例模式。

单例模式的缺点

1.单例模式一般没有接口,扩展很困难,若要扩展,基本上就是修改代码了。为嘛单例模式不能增加接口,因为接口对单例模式没有任何意义,它要求“自行实例化”,并且提供单一实例。接口或者抽象是不可能被实例化的(就是说设计接口或者抽象类不要套用单例模式,单例模式的‘自行实例化’和他俩的属性是冲突的)。在特殊情况下,单例模式可以实现接口、被继承等,需要在实际情况判断。

2.单例模式对于测试不利,单例未完成之前是不能进行测试的,没有接口也不能用mock方式虚拟一个对象。

mock测试就是在测试过程中,对于某些不容易构造或者不容易获取的对象,用一个虚拟的对象来创建以便测试的测试方法。http://baike.baidu.com/view/2445748.htm?fr=aladdin

3.单例模式与单一职责原则有冲突。一个类应该只实现一个逻辑,而不关心它是否是单例的,是不是单例取决于环境,单例模式把“要单例”和业务逻辑融合到一个类中。

单例模式适用于

1.要求生成唯一标识符的环境。

2.整个项目中需要一个共享访问点或者共享数据,例如一个web页面上的计数器,可以不用每次刷新都记录到数据库中,适用单例模式保持计数器的值,并确保是线程安全的。

3.创建一个对象需要消耗的资源过多。

4.需要定义大量的静态常量和静态方法(如工具类)的环境,可以采用单例模式,也可以直接声明为static方式。

下面是静态类和单例模式的讨论,有一个2004年csdn论坛上的老帖子http://bbs.csdn.net/topics/50351608

我实际中最常用的的静态类的莫过于工具类了,和不变类的定义类似,类中没有变量都是常量,只提供一些方法,不会因为状态而有所改变。而且一旦静态方法结束,其中的局部变量被jvm垃圾回收。

单例类含有状态,一般是需要共享的资源,譬如系统的配置信息等。把不变类设计成单例类就是单例模式最常见的滥用。

注意事项

1.在高并发的情况下,需要注意单例模式的线程同步问题。

//线程不安全的单例
publicclassSingleton{
privatestaticSingletonsingleton=null;
//限制产生多个对象
privateSingleton(){}
//通过该方式获得实例对象
publicSingletongetSingleton(){
if(singleton==null){
singleton=newSingleton();
}
returnsingleton;
}
}
/*低并发不会出现问题,一旦系统压力过大,并发量增加则可能在系统内存中出现多个实例。
线程A在执行到singleton=newSingleton(),还没有获得新对象,此时线程B执行
到了if判断,singleton为null,进入了if语句块,两个线程出现了两个实例。破坏了最初目的*/


改进,加上synchronized,这个改进型又叫做懒汉式单例,代码如下,但不是最优秀的。推荐使用饿汉单例模式

//懒汉单例模式
publicclassSingleton{
privatestaticSingletonsingleton=null;
//限制产生多个对象
privateSingleton(){}
//通过该方式获得实例对象
publicsynchronizedSingletongetSingleton(){
if(singleton==null){
singleton=newSingleton();
}
returnsingleton;
}
}


饿汉单例的测试

//fileSingleton.java
packageparctice;
publicclassSingleton{
privatestaticfinalSingletonsingleton=newSingleton();
privatestaticinti=1;
privateSingleton(){
this.i++;
}
publicstaticSingletongetInstance(){
returnsingleton;
}
publicstaticintgetI(){
returni;
}
}
//fileThreadTest.java
packageparctice;
publicclassThreadTest{
publicstaticvoidmain(String[]args){
Threadt1=newThread(newMyRunnable1());
Threadt2=newThread(newMyRunnable2());
t1.start();
t2.start();
}
}
classMyRunnable1implementsRunnable{
@Override
publicvoidrun(){
//TODOAuto-generatedmethodstub
Singletons=Singleton.getInstance();
System.out.println("MyRunnable1--"+s.getI());
}
}
classMyRunnable2implementsRunnable{
@Override
publicvoidrun(){
//TODOAuto-generatedmethodstub
Singletons=Singleton.getInstance();
System.out.println("MyRunnable2--"+s.getI());
}
}


结果截图



复习一下static静态变量、静态方法

内存总体一共分为了
4个部分(stacksegment、heapsegment、codesegment、datasegment)
当我们在程序中,申明一个局部变量的时候,此变量就存放在了stacksegment(栈)当中;
当new一个对象的时候,此对象放在了heapsegment(堆)当中;
而static的变量或者字符串常量则存在在datasegment(数据区)中;
那么类中方法的话,是存在在codesegment(代码区)中了。

在类Singleton被加载的时候,static类型的singleton变量就被存在了data
segment中,而不是随着类Singleton实例化出现在heapsegment中。在其他代码中被调用时,调用的总是datasegment的被存储的唯一变量singleton,因此对象被保证为单例。

单例模式的扩展

//有上限的多例模式
publicclassSingleton{
privatestaticfinalSingletonsingleton=newSingleton();
//实例的最大数量
privatestaticintmaxNumOfSingleton=5;
//一个数组容纳多个实例
privatestaticArrayList<Singleton>sinArr=newArrayList<Singleton>();
//静态代码块来实例化
static{
for(inti=0;i<maxNumOfSingleton;i++){
sinArr.add(newSingleton());
}
}
//限制产生多个对象
privateSingleton(){}
//通过该方法获得实例对象
publicstaticSingletongetSingleton(intindex){
returnsinArr.get(index);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: