首页 > 程序开发 > 软件开发 > 其他 >

单例模式之单对象创建

2017-03-11

单例模式之单对象创建:一个类只有一个实例存在,面向整个类。

单例模式之单对象创建:一个类只有一个实例存在,面向整个类。
如下代码:

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;
}

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

相关文章
最新文章
热点推荐