单例模式--单对象创建(创建型模式01)

来源:互联网 发布:海鹰数据库 编辑:程序博客网 时间:2024/05/01 02:40

什么是单例模式?
一个类只有一个实例存在,面向整个类。
如下代码:

public class ImageCache{public void getDownload(){}public void put(){}}
public class Image{  public void ImageDownload(){  //第一次实例化ImageCache类    ImageCache imgCache = new ImageCache();    imgCache.getDownload(){}  }  public void ImageStore(){  //第二次实例化ImageCahce类    ImageCache imgCache = new ImageCache();     imgCache.put(){}  }}

如上代码,我们两次实例化了ImageCache类,这就浪费了不必要的资源,为什么这样说?想象一下,假如ImageCache类中有成千上万行代码,包含线程池、缓存系统、网络请求等。那多次实例化将耗费大量的系统资源。单例模式要求实例化一次且面向整个类,所以优化后代码这样写:

public class Image{/**实例化一次,面向整个类可用,在方法内实例化是只能在作用域内使用,就是方法内。所以其他地方不能用,故要在*类的作用域内实例化,面向整个类都可用。*/ImageCache imgCache = new ImageCache();  public void ImageDownload(){     imgCache.getDownload(){}  }  public void ImageStore(){     imgCache.put(){}  }}

这就是单例模式,其实我们没有接触设计模式之前就知道这样做了,所以设计模式是一种学习经验。


单例模式进阶
单例模式要注意以下四点:

  • 构造函数一般为Private声明
  • 通过静态方法或枚举返回实例
  • 确保实例化对象的单一,尤其在多线程环境下
  • 确保实例化对象反序列化时不重构对象

关于Private声明和构造方法
Private为私有的意思,也就是你声明的变量只有自己能用,其他类不可调用。

public class ImageCache{/***public Imagecache(){}这就是构造方法:我们通过new ImageCache();就是执行这个方法来构造*ImageCache对象的,你写或者不写这个无参函数,系统都会默认执行,当然如果你这样构造public *Imagecache(String string){}这就是自定义的有参构造,这时候不写public Imagecache(){}系统*就认为你定义了有参构造函数,new的时候执行这个有参构造函数。*/public ImageCache(){}//相关代码片}

用Private声明构造函数:

public class ImageCache{private ImageCache(){}//相关代码片}

这样写之后,你会发现当你在其他类实例化ImageCache imgCache = new ImageCache();的时候他报错,因为private声明的只能本类使用,其他类无法使用。这样做的目的是防止你调用ImageCache类的时候他自动初始化。


1.懒汉模式:(用了才实例化,所以懒。。)

public class ImageCache{ private static ImageCache instance; private ImageCache(){} public static (synchronized) ImageCache getInstnce(){   if(instance == null){     instance = new ImageCache();    } return instance;   }}

加入synchronized关键字意思是线程同步,在多线程间操作同一对象时只能有一个线程操作,不会同时操作而引起问题。不加只适用于单线程。加了效率低。


2.饿汉模式(一开始使用类就实例化对象,所以饿。。)

public class ImageCache{ private static ImageCache instance = new ImageCache(); private ImageCache(){} public static ImageCache getInstnce(){  return instance;   }}

这种方式不用加锁,执行效率高点,因为类加载时候他就实例化实例了,当然会浪费内存。


3.双重锁校检模式

public class ImageCache{ private ImageCache(){}//构造函数私有化 private volatile static ImageCache instance = null//声明静态对象ImageCache public static ImageCache getInstance(){//静态方法实例化对象 if(instance == null){//首次判空      synchronized(ImageCache.class){//同步类         if(instance == null){//二次判空            instance = new ImageCache();//实例化对象         }      }  }  return instance; //返回对象}

这里进行了两次判空,作用是,synchronized同步需要消耗资源,如果在我们首次判断这个对象非空也就是实例化过的,就直接返回对象,跳过二次同步,想想一下如果没有外层判空每次我们是不是都要进行同步操作呢。这个同步尤其在多线程比较明显,它的作用就是让你的任务操作排队进行,不能同时操作。但是jvm允许执行乱序操作,所以他会出现1-2-3、1-3-2的顺序执行问题。所以官方给出了volatile关键字来解决这个问题,但是还是会影响点性能。优点是保证在多线程下保持高性能。


4.静态内部类来实现单例模式

public class ImageCache{ private ImageCache(){} public static ImageCache getInstance(){   return ImageCacheHolder.mInstance;   }/***静态内部类*/  private static class ImageCacheHolder{     private static final ImageCache mInstance = new ImageCache();  }}

这样设计当我们导入ImageCache类的时候,规避的问题有:调用类ImageCache的时候不会实例化,只有调用getInstance()方法才会实例化。如果instance操作耗费资源,这里实现延迟加载。但这种方式只适用于静态域。

ImageCache imgCache =ImageCache.getInstance();//静态方法在ImageCache已经发生实例化、静态内部类并没有只调用Holder类getInstance();//静态内部类在。getInstance()实例化

5.枚举实现单例模式
实现单例模式的最佳方法,但还未被广泛采用。

public enum ImageCache{INSTANCE;}

实例调用这样写:

ImageCache.INSTANCE;

支持自动序列化,防止多次实例化,默认线程安全。规避多线程问题。


1-4在反序列化时候会重新构造函数,枚举规避这种情况。要遇到需要加入如下方法返回实例,而不是新建:

private Object readResolve() throws ObjectStreamException{   return mInstance;}

无论我们以哪种方式实现单例模式,原理都是将构造函数私有化,通过静态方法获取唯一实例,再考虑线程安全和资源利用率等情况。具体取用哪一种,取决于项目本身,综合情况选择最佳的方式。

单例模式的优缺点

在内存中只有一个实例,减少内存开支。避免资源多重占用,比如对同一文件同时执行写操作。缺点是单例模式没有借口,扩展困难,除了修改代码,无法实现,这样就违背了开闭原则。

2 0
原创粉丝点击