设计模式(一)-单例模式

来源:互联网 发布:安卓新版淘宝指纹支付 编辑:程序博客网 时间:2024/05/18 03:23

一.单例模式作为23种设计模式之一,也属于三大类-创建型设计模式之一,其特点是对该类实例化只有一个对象

在Java应用中,单例对象能保证在一个JVM中,该对象只有一个实例存在。这样的模式有几个好处:
1、某些类创建比较频繁,对于一些大型的对象,这是一笔很大的系统开销。
2、省去了new操作符,降低了系统内存的使用频率,减轻GC压力。
3、有些类如交易所的核心交易引擎,控制着交易流程,如果该类可以创建多个的话,系统完全乱了。

单例模式的构建模式大体分为两类:饿汉式,懒汉式;

1.饿汉式

   class Singlton{

             //私有的构造方法

             private Singtlon(){}

             private static Singlton singlton = new Singlton();

            //提供一个公有方法给外部调用

            public static Singlton getSinglton(){

                 return singlton;    

             }

       }

2.懒汉式

  class Singlton{

             //私有的构造方法

             private Singtlon(){}

             private static Singlton singlton =null;

            //提供一个公有方法给外部调用

            public static Singlton getSinglton(){

                if(singlton==null){

                  singlton = new Singlton();

                }

                 return singlton;    

             }

       }


一些细节:

1.饿汉式比懒汉式线程安全;

2.关于懒汉式的线程安全的解决方案:

使用线程锁的方式,对于单例模式来说,临界资源是对象singtlon;

所以我们只要给临界资源上锁就可以啦!

  public static Singlton getSinglton(){

          if(singlton==null){

               synchronzied(singlton){

                  singlton = new Singlton();

               }

           }

                 return singlton;    

 }

3.还可以使用内部类来进行优化,JVM内部的机制能够保证当一个类被加载的时候,这个类的加载过程是线程互斥的(就是加载完毕后别的线程才能使用)。
这样当我们第一次调用getInstance的时候,JVM能够帮我们保证instance只被创建一次,
并且会保证把赋值给instance的内存初始化完毕,这样我们就不用担心上面的问题。
同时该方法也只会在第一次调用的时候使用互斥机制,这样就解决了低性能问题。

public class Singleton {  
 
/* 私有构造方法,防止被实例化 */  
private Singleton() {  
}  


/* 此处使用一个内部类来维护单例 */  
private static class SingletonFactory {  
   private static Singleton instance = new Singleton();  
}  


/* 获取实例 */  
public static Singleton getInstance() {  
   return SingletonFactory.instance;  
}  


/* 如果该对象被用于序列化,可以保证对象在序列化前后保持一致 */  
private Object readResolve() {  
   return getInstance();  
}  
}

4.还有一个反射的问题

反射中有一个方法setAccessiable(true)(暴力反射),

在私有构造方法中进行处理

private static boolean flag;

/* 私有构造方法,防止被实例化 */  
private Singleton() {  
if(!flag){
flag = false;
}else{
throw new RuntimeException("不能多次创建单例对象");
}
}  

反射的问题处理完了之后,这里还有一个问题,就是如果把单例对象进行序列化然后再反序列化,
那么内存中就会出现俩个一样的单例对象,只是内存地址不同。这种情况我们可以使用readResolve方法来防止。
private Object readResolve(){.....}
ObjectInputStream 会检查对象的class是否定义了readResolve方法。
如果定义了,将由readResolve方法指定返回的对象。返回对象的类型一定要是兼容的,否则会抛出ClassCastException 。 
例子:
public abstract class Singleton8 implements Serializable{  


private static final long serialVersionUID = 7863921642928237696L;


/* 此处使用一个内部类来维护单例 */  
private static class SingletonFactory {  
   @SuppressWarnings("serial")
private static Singleton8 instance = new Singleton8(){};
}  

//测试方式,把单例对象序列化后再反序列化从而获得一个新的对象 就相当于复制了一个单例对象
public Singleton8 copy() throws Exception{  
       ByteArrayOutputStream os = new ByteArrayOutputStream();  
       ObjectOutputStream oos = new ObjectOutputStream(os);  
       oos.writeObject(Singleton8.getInstance());  
         
       InputStream is = new ByteArrayInputStream(os.toByteArray());  
       ObjectInputStream ois = new ObjectInputStream(is);  
       Singleton8 obj = (Singleton8) ois.readObject();  
       return obj;  


/* 获取实例 */  
public static Singleton8 getInstance() {  
   return SingletonFactory.instance;  
}  


/* 如果该对象被用于序列化,可以保证对象在序列化前后保持一致 */  
/* 把这个方法注释前和注释后来运行测试代码观察结果 */  
private Object readResolve() {  
   return getInstance();  
}  
}
原创粉丝点击