作业18:单例模式
一 什么是单例?
顾名思义就是单个实例。
当你开出来的摸具,并使用摸具产生唯一一个模型,这就是单例。
而单例模式就是为了保证该类只产生一个实例。
二 什么时候需要单例?
简单说,你不需要多个实例的时候,就可以使用单例。
工具类一般都定义为单例,因为工具类不需要那么多个实例。
单例的好处:
- 节省维护对象的开销:创建、内存占用、回收
- 实例状态易于维护,注:一般单例不维护状态,当多个线程进行更改状态时,会产生并发问题。
三 如何实现单例?(不考虑序列化、反射)
1 饿汉式
public class Singleton{
private static final Singleton instance = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return instance;
}
}
2 懒汉式
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
3 静态内部类
public class Singleton {
private Singleton() {}
public static Singleton getInstance() {
return SingletonHolder.instance;
}
static class SingletonHolder {
private static final Singleton instance = new Singleton3();
}
}
4 枚举
public enum Singleton {
instance;
}
注:枚举本质上就是饿汉模式,具体参考作业16:java枚举类的秘密 。
四 单例补充
1 考虑反射
- 暴力反射能打破private限制,从而调用构造方法来创建对象。
public class Main {
public static void main(String[] args) throws Exception{
Class<? extends Singleton> aClass = Singleton.getClass();
Constructor<? extends Singleton> constructor = aClass.getDeclaredConstructor();
constructor.setAccessible(true); // 暴力反射
Singleton singleton = constructor.newInstance();
System.out.println(singleton == Singleton.getInstance()); // false
}
}
// 以饿汉为例的解决方案:构造器中抛出异常,防止对象创建成功
public class Singleton{
private static final Singleton instance = new Singleton();
private Singleton() {
if (instance != null)
throw new RuntimeException("private constructor");
}
public static Singleton getInstance() {
return instance;
}
}
2 考虑序列化
public class Main {
public static void main(String[] args) throws IOException, ClassNotFoundException {
// 序列化
ObjectOutputStream oo = new ObjectOutputStream(new FileOutputStream(new File("singleton.txt")));
oo.writeObject(Singleton.getInstance());
oo.close();
// 反序列化
ObjectInputStream oi = new ObjectInputStream(new FileInputStream(new File("singleton.txt")));
Singleton singleton = (Singleton) oi.readObject();
oi.close();
System.out.println(singleton == Singleton.getInstance()); // false
}
}
// 以饿汉为例的解决方案:添加readResolve方法
public class Singleton implements Serializable {
private static final Singleton instance = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return instance;
}
private Object readResolve() {
return instance;
}
}
参考资料: