现在的位置: 首页 > 综合 > 正文

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

2019年09月13日 ⁄ 综合 ⁄ 共 7187字 ⁄ 字号 评论关闭

实现单例模式的思路是:

1,一个类能返回对象一个引用(永远是同一个)和一个获得该实例的方法(必须是静态方法,通常使用getInstance这个名称);

2,当我们调用这个方法时,如果类持有的引用不为空就返回这个引用,如果类保持的引用为空就创建该类的实例并将实例的引用赋予该类保持的引用;

3,同时我们还将该 类的构造函数定义为私有方法,这样其他处的代码就无法通过调用该类的构造函数来实例化该类的对象,只有通过该类提供的静态方法来得到该类的唯一实例。


1.饿汉式。指全局的单例实例在类装载时构建,当类第一次加载到内存中的时候就初始化了,所以创建的实例固然是thread-safe

package com.open.design.singleton;

/**
 * 饿汉式
 * @author Administrator
 * 
 */
public class Singleton1 {

		private final static Singleton1 instance=new Singleton1();
		
		private Singleton1(){}
		
		public static Singleton1 getInstance()
		{
			return instance;
		}
}

package com.open.design.singleton;

/**
 * 属于饿汉式,静态初始化。
 * @author Administrator
 *
 */
public class Singleton5 {
    private static final Singleton5 instance;
 
    static {
        try {
            instance = new Singleton5();
        } catch (Exception e) {
            throw new RuntimeException("Darn, an error occurred!", e);
        }
    }
 
    private Singleton5() {}
    
    public static Singleton5 getInstance() {
        return instance;
    }
}

2.懒汉式。

package com.open.design.singleton;

/**
 *  懒汉式方式1
 * @author Administrator
 *
 */
public class Singleton2 {

	private static Singleton2 instance = null;
	 
	private Singleton2() {}
 
	public static Singleton2 getInstance() 
	{
		if (instance == null) 
		{
             instance = new Singleton2 ();
		}
		return instance;
	}

}

package com.open.design.singleton;

/**
 *  懒汉式方式2
 * @author Administrator
 *
 */
public class Singleton3 {
	
		private static Singleton3 instance = null;
	 
		private Singleton3() {}
	 
		public static synchronized Singleton3 getInstance() 
		{
			if (instance == null) 
			{
	             instance = new Singleton3 ();
			}
			return instance;
		}
}

package com.open.design.singleton;

/**
 *  懒汉式方式3
 * @author Administrator
 *
 */
public class Singleton4 {
	
	private static volatile Singleton4 INSTANCE = null;
	
	// Private constructor suppresses 
    // default public constructor
    private Singleton4() {}
 
    //thread safe and performance  promote 
    public static  Singleton4 getInstance() 
    {
        if(INSTANCE == null)
        {
             synchronized(Singleton4.class)
             {
                 //when more than two threads run into the first null check same time, to avoid instanced more than one time, it needs to be checked again.
                 if(INSTANCE == null)
                 { 
                     INSTANCE = new Singleton4();
                  }
              } 
        }
        return INSTANCE;
    }
    
 }

(懒汉式方法3只能用在JDK5及以后版本(注意 INSTANCE 被声明为volatile),之前的版本使用“双重检查锁”会发生非预期行为[1]

3.虚拟机同步保证。完全使用了Java虚拟机的机制进行同步保证,没有一个同步的关键字。这种方式是Singleton类被装载了,instance不一定被初始化。因为SingletonHolder类没有被主动使用,只有显示通过调用getInstance方法时,才会显示装载SingletonHolder类,从而实例化instance。

package com.open.design.singleton;

/**
 * 完全使用了Java虚拟机的机制进行同步保证,没有一个同步的关键字。
 * @author Administrator
 *
 */
public class Singleton6 {
	
	// Private constructor prevents instantiation from other classes
	private Singleton6() { }
 
	/**
	* SingletonHolder is loaded on the first execution of Singleton.getInstance() 
	* or the first access to SingletonHolder.INSTANCE, not before.
	*/
	private static class SingletonHolder { 
		public static final Singleton6 INSTANCE = new Singleton6();
	}
 
	public static Singleton6 getInstance() {
		return SingletonHolder.INSTANCE;
	}
}

4.枚举写法。因为创建枚举默认就是线程安全的,这个是针对jdk 1.5以及1.5版本以上的

package com.open.design.singleton;

/**
 * 枚举写法
 * @author Administrator
 *
 */
public enum Singleton7 {
	INSTANCE;
}

1.反射问题,除了枚举单例外,其它的单例方式均可以通过反射获取新的单例对象。

package com.open.design.singleton;

import java.lang.reflect.Constructor;

public class TestSingleton {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		
		testSingleton1();
		testSingleton2();
		testSingleton3();
		testSingleton4();
		testSingleton5();
		testSingleton6();
		testSingleton7();
	}
	
	public static void testSingleton1()
	{
		Singleton1 ins=Singleton1.getInstance();//得到第一个实例 
		System.out.println(ins);
		
		Singleton1 ins2=null;//用反射得到第二个实例,这里引用类的时候得写全路径,否则会报找不到类
		try {
			Class c = Class.forName("com.open.design.singleton.Singleton1");
			Constructor[] con = c.getDeclaredConstructors();
			Constructor conc = con[0]; 
			conc.setAccessible(true);
			ins2 = (Singleton1)conc.newInstance(); 
			System.out.println(ins2);
			
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		System.out.println("testSingleton1:"+(ins.equals(ins2)? "same":"diff"));
		System.out.println();
	}

	public static void testSingleton2()
	{
		Singleton2 ins=Singleton2.getInstance();//得到第一个实例 
		System.out.println(ins);
		
		Singleton2 ins2=null;
		try {
			Class c = Class.forName("com.open.design.singleton.Singleton2");//用反射得到第二个实例,这里引用类的时候得写全路径,否则会报找不到类
			Constructor[] con = c.getDeclaredConstructors();
			Constructor conc = con[0]; 
			conc.setAccessible(true);
			ins2 = (Singleton2)conc.newInstance(); 
			System.out.println(ins2);
			
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		System.out.println("testSingleton2:"+(ins.equals(ins2)? "same":"diff"));
		System.out.println();
	}
	
	public static void testSingleton3()
	{
		Singleton3 ins=Singleton3.getInstance();//得到第一个实例 
		System.out.println(ins);
		
		Singleton3 ins2=null;
		try {
			Class c = Class.forName("com.open.design.singleton.Singleton3");//用反射得到第二个实例,这里引用类的时候得写全路径,否则会报找不到类
			Constructor[] con = c.getDeclaredConstructors();
			Constructor conc = con[0]; 
			conc.setAccessible(true);
			ins2 = (Singleton3)conc.newInstance(); 
			System.out.println(ins2);
			
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		System.out.println("testSingleton3:"+(ins.equals(ins2)? "same":"diff"));
		System.out.println();
	}
	
	public static void testSingleton4()
	{
		Singleton4 ins=Singleton4.getInstance();//得到第一个实例 
		System.out.println(ins);
		
		Singleton4 ins2 =null;
		try {
			Class c = Class.forName("com.open.design.singleton.Singleton4");//用反射得到第二个实例,这里引用类的时候得写全路径,否则会报找不到类
			Constructor[] con = c.getDeclaredConstructors();
			Constructor conc = con[0]; 
			conc.setAccessible(true);
			ins2 = (Singleton4)conc.newInstance(); 
			System.out.println(ins2);
			
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		System.out.println("testSingleton4:"+(ins.equals(ins2)? "same":"diff"));
		System.out.println();
	}
	
	public static void testSingleton5()
	{
		Singleton5 ins=Singleton5.getInstance();//得到第一个实例 
		System.out.println(ins);
		
		Singleton5 ins2=null;
		try {
			Class c = Class.forName("com.open.design.singleton.Singleton5");//用反射得到第二个实例,这里引用类的时候得写全路径,否则会报找不到类
			Constructor[] con = c.getDeclaredConstructors();
			Constructor conc = con[0]; 
			conc.setAccessible(true);
			ins2 = (Singleton5)conc.newInstance(); 
			System.out.println(ins2);
			
		} catch (Exception e) {
			e.printStackTrace();
		}
		System.out.println("testSingleton5:"+(ins.equals(ins2)? "same":"diff"));
		System.out.println();
	}
	
	public static void testSingleton6()
	{
		Singleton6 ins=Singleton6.getInstance();//得到第一个实例 
		System.out.println(ins);
		
		Singleton6 ins2=null;
		try {
			Class c = Class.forName("com.open.design.singleton.Singleton6");//用反射得到第二个实例,这里引用类的时候得写全路径,否则会报找不到类
			Constructor[] con = c.getDeclaredConstructors();
			Constructor conc = con[0]; 
			conc.setAccessible(true);
			ins2 = (Singleton6)conc.newInstance(); 
			System.out.println(ins2);
			
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		System.out.println("testSingleton6:"+(ins.equals(ins2)? "same":"diff"));
		System.out.println();
	}
	
	public static void testSingleton7()
	{
		Singleton7 ins=Singleton7.getInstance();//得到第一个实例 
		System.out.println(ins);
		
		Singleton7 ins2=null;
		try {
			Class c = Class.forName("com.open.design.singleton.Singleton7");//用反射得到第二个实例,这里引用类的时候得写全路径,否则会报找不到类
			Constructor[] con = c.getDeclaredConstructors();
			Constructor conc = con[0]; 
			conc.setAccessible(true);
			ins2 = (Singleton7)conc.newInstance(); 
			System.out.println(ins2);
			
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		System.out.println("testSingleton7:"+(ins.equals(ins2)? "same":"diff"));
		System.out.println();
	}
}

查看打印结果,发现枚举类单例通过反射获取失败,其它单例形式反射成功,证明枚举类型最安全。

2.序列化问题

其中单例1.2.3存在的一个问题是一旦你实现了序列化接口,那么它们不再保持单例了,因为readObject()方法一直返回一个新的对象就像java的构造方法一样,你可以通过使用readResolve()方法来避免此事发生,看下面的例子:

  //readResolve to prevent another instance of Singleton

    privateObject readResolve()

    {
        returnINSTANCE;
    }

 
  甚至还可以更复杂,如果你的单例类维持了其他对象的状态的话,因此你需要使他们成为transient的对象。但是枚举单例(单例方式4),JVM对序列化有保证。

3.如果单例由不同的类装载器装入,那便有可能存在多个单例类的实例。

http://www.importnew.com/6461.html

http://blog.csdn.net/kerryyw/article/details/7237821

http://sucre.blog.51cto.com/1084905/569511

http://www.blogjava.net/kenzhh/archive/2013/03/15/357824.html

代码地址:https://github.com/zz7zz7zz/design-pattern

抱歉!评论已关闭.