单例模式
描述
单例模式: 想要这个类产生的对象永远只有一个,或者说产生的对象指向同一个内存地址。
单例模式实现方式千千万,本文提供2种常用的和1种简单直观的解决方案。
饿汉式
单例模式的关键点就是要私有构造,不能让其直接new出对象,而是提供一个静态的公有方法提供对象。
饿汉式的关键是,在类中提供了一个成员对象,以后你想要创建对象,就把这个对象拉出去用。
// 饿汉
public class Singleton1 {
// 提供不为 null 的成员属性
private static Singleton1 s = new Singleton1();
// 私有构造
private Singleton1() {}
// 提供方法用于给外面类获取对象
public static Singleton1 getInstance() {
return s;
}
}
懒汉式
懒汉式提供的成员对象是null,你要想创建对象,就调用 getInstance()
来创建对象。
关键点:synchronized
为什么方法要加同步锁呢?
为了安全性考虑的,如果 A
线程进入方法时,创建对象前, B
线程同时也进入了方法,它们判断对象有可能都是null,这时创建的两个对象就会指向两个不同的内存空间。
// 懒汉
public class Singleton2 {
// 成员属性初始为null
private static Singleton2 s;
// 私有构造
private Singleton2() {}
// 提供方法创建对象
public synchronized static Singleton2 getInstance() {
return s == null ? new Singleton2() : s;
}
}
枚举实现
枚举实现的思路其实就是饿汉式的实现方式,使用枚举是为了代码简洁。
还有一个问题,之前两种方式,我们虽然私有了构造,但是一定就创建不了对象吗,反射可以做到。
枚举本身就只能存在私有构造,而且反射也无法创建枚举对象。
// 枚举实现单例
public enum EnumSingleton {
// 不为 null 的本类成员变量
SINGLETON;
// 提供静态方法返回对象
public static EnumSingleton getInstance() {
return SINGLETON;
}
}
优缺点
- 饿汉式没有加锁,效率相对高,但是初始化就创建了对象,浪费内存一些;
- 懒汉式第一次调用方法才创建对象,节约内存,但是加了锁,效率相对低;
- 枚举实现代码简洁,而且解决了反射问题,更加安全。