##单例模式 单例模式在软件开发中经常用到,是一种常见的开发模式. 此模式和spring中的单例作用域是不同的概念,在spring中,只是保证此对象的spring作用的 上下文里是一个实例。 单例模式UML图:

单例模式有很多种写法,这里实现几种简单的写法,并做简单的分析,记录! ###1、饿汉式


package com.tony.base.patterns.singleton;

import java.lang.reflect.Constructor;

/**
 * 
 * 饿汉式 . 缺点:<br>
 * 1、没有lazy Load,在类被加载的时候就实例化对象。<br>
 * 2、可以通过反射得到许多新的实例对象.
 * 
 * @author TONY
 */
public class Singleton1 {

	/**
	 * 通过私有的构造方法保证此类不能被new 出对象来
	 */
	private Singleton1() {
	}

	/**
	 * 使用static 关键字,保证此类加载的时候对instance变量初始化一次,可以保证此变量只有一个.
	 * <p>
	 * 对于 new Singleton1() 这个对象是分配在堆内存里的,但是此变量被static修饰,<br>
	 * 属于类变量(全局变量) 被类的class 对象引用,而类的class对象被Class loader引用,<br>
	 * 从而保证new 出的对相关在堆中不能垃圾回收器回收.
	 * </p>
	 * 如果想回收掉此对象,需要卸载加载此类的class loader,同时永久代中的class被回收掉 ,<br>
	 * 此对象就可以被垃圾回收器回收掉.
	 */
	private static Singleton1 instance = new Singleton1();

	/**
	 * 提供一个对外公开访问的方法,返回此实例对象.
	 * 
	 * @return instance {@code Singleton1}.
	 */
	public static Singleton1 getInstance() {
		return instance;
	}

	public static void main(String[] args) throws Exception {
		/* 正常的通过静态方法获取对象,每次获取的都是同一个对象. */
		Singleton1 sg1 = Singleton1.getInstance();
		Singleton1 sg2 = Singleton1.getInstance();
		System.out.println("通过单例模式获取的对象equals比较:" + sg1.equals(sg2));
		System.out.println("单例模式实例对象1:" + sg1);
		System.out.println("单例模式实例对象2:" + sg2);

		/* 通过反射获取实例对象,可以构造出多个实例对象出来 */
		Constructor<Singleton1> con = Singleton1.class.getDeclaredConstructor();
		con.setAccessible(true);
		Singleton1 s1 = con.newInstance();
		Singleton1 s2 = con.newInstance();
		System.out.println("通过反射获取单例对象equals比较:" + s1.equals(s2));
		System.out.println("反射获取的对象1:" + s1);
		System.out.println("反射获取对象2:" + s2);
	}
}

###2、懒汉式 懒汉式:主要是保证实例的创建,在使用的时候才初始化一次。如果不使用,此实例不会被初始化。

####2.1同步方法写法 分析过程如第一种单例代码里。


public class Singleton2 {

	private Singleton2() {
	}

	private static Singleton2 instance;

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

####2.2双检锁写法 此种写法,避免代码重排。使用volaticle 关键字,遵照happen-before 法则,从而可以保证 实例对象不会被创建多次.


public class Singleton3 {

	private Singleton3() {
	}

	public static volatile Singleton3 instance;

	public Singleton3 getInstance() {
		if (instance == null) {
			synchronized (Singleton3.class) {
				if (instance == null) {
					instance = new Singleton3();
				}
			}
		}
		return instance;
	}

}

###3、通过内部类的方式实现单例 此种方式使用一个静态内部类去实现单例的对象创建,静态内部类声明为私有的,只有在当前类里可以调用 所以,SingletonHolder 的初始化只有在调用getInstance()方法时才能被初始化。 由于是静态的,分析过程请查看第一种单例的代码中的注释。


public class Singleton4 {

	private Singleton4() {
	}

	private static class SingletonHolder {
		public static Singleton4 instance = new Singleton4();
	}

	public static Singleton4 getInstance() {
		return SingletonHolder.instance;
	}
	
	public static void main(String[] args) {
		Singleton4 s1 = Singleton4.getInstance();
		Singleton4 s2 = Singleton4.getInstance();
		System.out.println("单例对象1:"+s1);
		System.out.println("单例对象2:"+s2);
		System.out.println("单例对象比较"+s1.equals(s2));
		
	}

###4、通过枚举实现单例Enum 枚举类型是无法通过反射获取实例对象的,这个在java.lang.reflect.Constructor 的 newInstance(Object….obj) 方法里做了判断。 下面是摘自Constructor类中的代码片段:


    @CallerSensitive
    public T newInstance(Object ... initargs)
        throws InstantiationException, IllegalAccessException,
               IllegalArgumentException, InvocationTargetException
    {
        if (!override) {
            if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
                Class<?> caller = Reflection.getCallerClass();
                checkAccess(caller, clazz, null, modifiers);
            }
        }
        if ((clazz.getModifiers() & Modifier.ENUM) != 0)
            throw new IllegalArgumentException("Cannot reflectively create enum objects");
        ConstructorAccessor ca = constructorAccessor;   // read volatile
        if (ca == null) {
            ca = acquireConstructorAccessor();
        }
        return (T) ca.newInstance(initargs);
    }

很明显,如果被反射创建对象的是枚举类,则抛出异常IllegalArgumentException,因此,枚举作为单例来说有自己的是可以保证别人无法创建 额外的对象实例的。
下面是代码:其实可以不提供getInstance() 方法的,直接使用类名调用INSTANCE 即可.


public enum Singleton5 {
	INSTANCE;
	public static Singleton5 getInstance() {
		return INSTANCE;
	}
}

枚举编译后的代码.(通过java -c -p Singleton5.class 可以查看到)