设计模式—单例模式

104 阅读2分钟
采取一定的方法保证某个类在整个软件系统中,只能存在一个实例

使用场景:需要频繁创建和销毁的对象;重量级对象

第一种:饿汉式

/**
 * 饿汉式(静态变量)
 * @author 
 *
 */
public class Singleton {

	//1.私有化构造
	private Singleton() {
	}

	// 2.提供静态变量,还有一种写法是在静态代码块中新建对象赋值,两种没啥区别
	private final static Singleton instance = new Singleton();

	//3.对外提供接口
	public static Singleton getInstance() {
		return instance;
	}
}

饿汉式:静态变量加载

优点:类装载时完成实例化,避免了线程同步的问题

缺点:如果从始至终没有使用过该对象,就会造成性能浪费,没有达到懒加载的效果



第二种:懒汉式

/**
 * 懒汉式
 * 
 * @author
 *
 */
public class Singleton {

	// 1.私有化构造
	private Singleton() {
	}

	// 2.提供静态变量
	private static Singleton instance;

	// 3.对外提供接口
	public static Singleton getInstance() {
		if (instance == null) {
			instance = new Singleton();
		}
			return instance;
	}
}

最初版本的懒汉式

优点:实现了懒加载

缺点:只能在单线程情况下使用,当有多个线程同时进入 instance == null判断,可能会产生多个对象

解决方法:在getInstance方法上直接加synchronized锁,但是效率太低


双检锁的懒汉式:推荐使用

public class Singleton {

	// 1.私有化构造
	private Singleton() {
            //防止反射
		if (instance != null) {
			throw new RuntimeException("禁止创建");
		}
	}

	// 2.提供静态变量
	private static volatile Singleton instance;

	// 3.对外提供接口
	public static Singleton getInstance() {
		if (instance == null) {
			synchronized (Singleton.class) {
				if (instance == null) {
					instance = new Singleton();
				}
			}

		}
		return instance;
	}
}

推荐使用:解决了线程安全和懒加载的问题,并且效率比最初版本的懒汉式高得多


使用静态内部类完成单例:推荐使用

public class Singleton {

	// 1.私有化构造
	private Singleton() {
	}

	// 2.静态内部类,静态变量变量
	public static class SingletonInstance{
		private static Singleton INSTANCE = new Singleton();
	}

	// 3.对外提供接口
	public static Singleton getInstance() {
		return SingletonInstance.INSTANCE;
	}
}

静态内部类在装载时并不会立即实例化,而是在需要使用他也就是调用 getInstance()的时候才会被初始化,由此实现了懒加载。

类的静态属性只有在类第一次加载的时候初始化,在这里,JVM帮助我们实现了线程安全,在类进行初始化时,别的线程无法进入



使用反射机制,破坏懒汉式单例,可以在构造方法中判断,抛出异常来防止反射破坏单例

	public static void destory() throws Exception {
                //获取单例对象
		Singleton instance = Singleton.getInstance();
                //拿到私有化的构造方法
		Constructor<? extends Singleton> constructor = instance.getClass()
				.getDeclaredConstructor();
                //越过语法检查
		constructor.setAccessible(true);
                //新建第二对象
		Singleton newInstance = constructor.newInstance();
		//结果为false
		System.out.println(instance==newInstance);
	}



枚举类型的天生单例,可以抗反射和反序列化,推荐使用

public enum SingletonEnum {
	//创建一个枚举对象,该对象天生单例
	Instance;
	
}