疯狂java


您现在的位置: 疯狂软件 >> 新闻资讯 >> 正文

设计模式-单例模式的分析


 

在JDK1.5之前,实现Singleton有两种方法。这两种方法都要把构造器保持为私有的,并导出公有的静态成员,以便允许客户端能够访问该类的唯一实现。
 
 
1.暴露静态final字段
public class Singleton {
 
  private static Singleton instance=new Singleton();
  private Singleton(){
 
  }
  static Singleton getInstance() {
      return instance;
  }
}
不过上述方式是不能保证绝对的只有一个实例,通过反射机制可以突破这一点,所以该方法不推荐传说中的饿汉式的单例模式。
 
2.暴露静态方法
public class Singleton1 {
private static Singleton1 instance=null;
 private Singleton1(){}
 static Singleton1 getInstance() {
     if(instance==null)
     instance=new Singleton1();
     return instance;
 }
}
 
对于静态方法getInstance()方法的所有调用,都会返回同一个对象的应用。这种写法的好处是很清楚地表明了该类是一个Singleton,但是这种写法有另一个好处是,可以添加其他的静态工厂方法返回另一个新的实例,这样就可以将该类变为非Signleton。
 
for(Constructor<?> c:s1.getClass().getDeclaredConstructors()){
c.setAccessible(true);
try {
Singleton s3=(Singleton)c.newInstance();
System.out.println(s3==s2);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
这是通过反射可以创建一个对象,反射技术是破坏单例模式的。private只在编译时进行权限的限制,但是在运行时是不存在这种权限的限制的,这些权限的限制是由运行时的安全机制来检查,但是这种安全很脆弱。要解决这个问题,我觉得把单例定义成为抽象类,这样就不可以通过反射来实例化。在类的厂方法内部实现一个匿名内部类来继承单例类返回单例类实例。
要是通过实习序列化接口也会产生,通过反序列化可以产生多个对象,在实现序列化的类里添加方法。想得到单例必须
  private Object readResolve() throws ObjectStreamException{
    return INSTANCE;//返回该类的单例对象
  }
这样当JVM从内存中反序列化地"组装"一个新对象时,就会自动调用这个 readResolve方法来返回我们指定好的对象
 
 
对于懒加载的这种方式是线程不安全的。需要加一个Synchronized,
 
  public static Synchronized Singleton1 getInstance(){  
        if(null == singObj ) singObj = new Singleton1();
            return singObj;
    }  
不过同步的代价必然会一定程度的使程序的并发度降低,可以通过双重检查锁把效率提高。
  public static Singleton1 getInstance(){  
        if(null == singObj ) {
              Synchronized(Singleton1.class){
                     if(null == singObj)
                           singObj = new Singleton1();
              }
         }
       return singObj;
    }   
 
 
从JDK1.5开始,实现Singleton还有第三种方法,只需编写一个包含单个元素的枚举类型。推荐的写法:
 
 
public enum Singleton {  
    INSTANCE;  
    public void whateverMethod() {  
 
    }  
}
 
 
单元素的枚举类型已经成为实现Singleton的最佳方法。使用enum关键字来实现单例模式的好处是这样非常简洁,并且无偿地提供了序列化机制,绝对防止多次实例化,即使是在面对复杂的序列化或者反射攻击的时候。