背景
单例模式是创建型的设计模式,在项目中大概率会用到,那么怎么用好单例模式呢,下面分别列出常见的一些单例模式写法,以及一些破坏单例模式的处理办法,供大家学习参考。
饿汉式
package com.example.pattern.hungry;
/**
* 缺点:如果工程中饿汉式单例特别多,一些用不到的实例就比较浪费内存。
*/
public class HungryPattern {
private static final HungryPattern hungryPattern = new HungryPattern();
private HungryPattern() {
}
public static HungryPattern getInstance() {
return hungryPattern;
}
}
懒汉式写法1
package com.example.pattern.lazy;
/**
* 懒汉式的优点:使用的时候才初始化实例,减少了不必要的内存浪费
*/
public class LazyPattern {
//使用volatile是为了防止指令重排序
private static volatile LazyPattern lazyPattern = null;
private LazyPattern() {
//防止反射破坏
if (lazyPattern != null) {
throw new RuntimeException("Not can use Construct Init...");
}
}
public static LazyPattern getInstance() {
if (lazyPattern == null) {
synchronized (LazyPattern.class) {
if (lazyPattern == null) {
lazyPattern = new LazyPattern();
}
}
}
return lazyPattern;
}
}
懒汉式写法2
package com.example.pattern.lazy;
public class LazyStaticPattern {
private LazyStaticPattern() {
//防止反射破坏
if (LazyStaticInner.lazyStaticPattern != null) {
throw new RuntimeException("Not can use Construct Init...");
}
}
public static LazyStaticPattern getInstance() {
return LazyStaticInner.lazyStaticPattern;
}
private static class LazyStaticInner {
private static final LazyStaticPattern lazyStaticPattern = new LazyStaticPattern();
}
}
原理:静态内部类只有在显式调用时才会被 JVM 加载和初始化,因此实现了懒加载。
线程安全:由于类的初始化是由 JVM 管理的,因此在类初始化时是线程安全的
防止序列化破坏:但如果需要防止序列化破坏单例,可以重写 readResolve() 或 readObject() 方法来确保返回单例对象。
枚举类型单例
package com.example.pattern.register;
import org.omg.PortableInterceptor.INACTIVE;
public enum EnumPattern {
INACTIVE;
private Object object;
public static EnumPattern getInstance() {
return INACTIVE;
}
}
单例放序列化攻击
package com.example.pattern.serialzable;
import com.example.pattern.lazy.LazyPattern;
import java.io.*;
public class SerializablePattern implements Serializable {
private static SerializablePattern serializablePattern = new SerializablePattern();
private SerializablePattern() {
}
public static SerializablePattern getInstance() {
return serializablePattern;
}
//防止序列化破坏单例模式
public Object readResolve() {
if (serializablePattern != null) {
return serializablePattern;
}
return null;
}
public static void main(String[] args) {
SerializablePattern serializablePattern1 = SerializablePattern.getInstance();
try {
//先将对象以字节码的方式写入到文件中
FileOutputStream fos = new FileOutputStream(new File("serializable.obj"));
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fos);
objectOutputStream.writeObject(serializablePattern1);
System.out.println(serializablePattern1.toString());
//再从文件中读取字节码文件转成对象
FileInputStream fis = new FileInputStream(new File("serializable.obj"));
ObjectInputStream objectInputStream = new ObjectInputStream(fis);
SerializablePattern serializablePattern2 = (SerializablePattern) objectInputStream.readObject();
System.out.println(serializablePattern2.toString());
} catch (Exception e) {
e.printStackTrace();
}
}
}
注意:
还可以使用ioc和threadlocal进行单例的获取,分别具有不同的特点,由于篇幅较多,这里就不做说明了