- 定義 : 保證一個類僅有一個實例,並提供一個全局訪問點
- 類型 : 創建型
- 適用場景 : 想確保任何情況下都絕對只有一個實例
- 優點 : 在內存裏只有一個實例,減少了內存開銷;可以避免對資源的多重佔用;設置全局訪問點,嚴格控制訪問
- 缺點 : 沒有接口,擴展困難
- 重點 : 私有構造器;線程安全;延遲加載;序列化和反序列化安全;反射
1 懶漢式(線程不安全)[不可用]
在多線程模式下,懶漢式由於線程不安全是不可用的
public class Singleton {
private static Singleton instance;
//私有化構造器
private Singleton() {
try {
//創建對象 耗時操作
TimeUnit.SECONDS.sleep(new Random().nextInt(5));
} catch (InterruptedException ignored) {}
}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
@Test
public void testThreadVis() throws IOException {
Runnable task = () -> {
Singleton instance = Singleton.getInstance();
System.out.println(Thread.currentThread().getName() + " " + instance);
};
new Thread(task, "線程一").start(); //線程一 Singleton@46385f83
new Thread(task, "線程二").start(); //線程二 Singleton@4ef99cce
System.in.read();
}
2 反序列化破壞單例模式
/**
* 餓漢式(靜態常量)
*/
//這個單例類實現了序列化接口
public class Singleton implements Serializable {
private final static Singleton instance = new Singleton();
// 構造函數私有化
private Singleton() {}
public static Singleton getInstance() {
return instance;
}
}
//反序列化破壞單例模式
@Test
public void test() throws IOException, ClassNotFoundException {
Singleton instance = Singleton.getInstance();
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("singleton_file"));
oos.writeObject(instance);
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("singleton_file")));
Singleton deserializationObj = (Singleton) ois.readObject();
System.out.println(instance); // Singleton@5e8c92f4
System.out.println(deserializationObj); // Singleton@244038d0
}
3 readResolve 避免反序列化
/**
* 餓漢式(靜態常量)
*/
//這個單例類實現了序列化接口
public class Singleton implements Serializable {
private final static Singleton instance = new Singleton();
// 構造函數私有化
private Singleton() { }
public static Singleton getInstance() {
return instance;
}
//
private Object readResolve(){
return instance;
}
}
//聲明readResolve方法,防止反序列化破壞單例模式
@Test
public void test() throws IOException, ClassNotFoundException {
Singleton instance = Singleton.getInstance();
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("singleton_file"));
oos.writeObject(instance);
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("singleton_file")));
Singleton deserializationObj = (Singleton) ois.readObject(); //打斷點
System.out.println(instance); // Singleton@5e8c92f4
System.out.println(deserializationObj); // Singleton@5e8c92f4
System.out.println(instance == deserializationObj);
}
//java.io.ObjectInputStream#readObject
public final Object readObject() {
if (enableOverride) {
return readObjectOverride();
}
// if nested read, passHandle contains handle of enclosing object
int outerHandle = passHandle;
try {
Object obj = readObject0(false); //打斷點
//java.io.ObjectInputStream#readObject0
private Object readObject0(boolean unshared) throws IOException {
try {
switch (tc) {
case TC_OBJECT:
return checkResolve(readOrdinaryObject(unshared)); //打斷點
//java.io.ObjectStreamClass#invokeReadResolve
private Object readOrdinaryObject(boolean unshared) {
Object obj;
try {
//創建對象
obj = desc.isInstantiable() ? desc.newInstance() : null;
} catch (Exception ex) {}
if (obj != null && handles.lookupException(passHandle) == null && desc.hasReadResolveMethod()) {
//反射調用readResolve方法
Object rep = desc.invokeReadResolve(obj);
if (unshared && rep.getClass().isArray()) {
rep = cloneArray(rep);
}
if (rep != obj) {
// Filter the replacement object
if (rep != null) {
if (rep.getClass().isArray()) {
filterCheck(rep.getClass(), Array.getLength(rep));
} else {
filterCheck(rep.getClass(), -1);
}
}
handles.setObject(passHandle, obj = rep);
}
}
return obj;
}
4 反射破壞單例模式
public class Singleton {
private final static Singleton instance = new Singleton();
// 構造函數私有化 反射可以破壞
private Singleton() {}
public static Singleton getInstance() {
return instance;
}
}
@Test
public void test() throws Exception {
Singleton instance = Singleton.getInstance();
Class<Singleton> objClass = Singleton.class;
Constructor<Singleton> constructor = objClass.getDeclaredConstructor();
constructor.setAccessible(true); //java.lang.IllegalAccessException: Class SingletonTest can not access a member of class Singleton with modifiers "private"
Singleton reflexObj = constructor.newInstance();
//
System.out.println(instance); //Singleton@32a1bec0
System.out.println(reflexObj); //Singleton@22927a81
System.out.println(reflexObj == instance); //false
}
5 反射防禦
public class Singleton {
private final static Singleton instance = new Singleton();
// 構造函數私有化
private Singleton() {
//反射防禦 只有餓漢式、靜態內部類 纔可用
if (instance != null) {
throw new RuntimeException("單例構造器禁止反射調用");
}
}
public static Singleton getInstance() {
return instance;
}
}
public class Singleton {
private Singleton() {
if (SingletonInstance.INSTANCE != null) {
throw new RuntimeException("單例構造器禁止反射調用");
}
}
private static class SingletonInstance {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonInstance.INSTANCE;
}
}
6 反射防禦失敗
/**
* 懶漢式(線程安全)(不推薦)
*/
public class Singleton {
private static Singleton instance;
private Singleton() {
if (instance != null) {
throw new RuntimeException("單例構造器禁止反射調用");
}
}
public synchronized static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
@Test
public void test() throws Exception {
Class<Singleton> objClass = Singleton.class;
Constructor<Singleton> constructor = objClass.getDeclaredConstructor();
constructor.setAccessible(true);
//如果反射方法先執行,依然是會有兩個不同的對象的
Singleton reflexObj = constructor.newInstance();
Singleton instance = Singleton.getInstance();
System.out.println(instance); // Singleton@32a1bec0
System.out.println(reflexObj); // Singleton@22927a81
System.out.println(reflexObj == instance); //false
}
可能你想到用可以變量來標記私有化構造器執行了幾次,然後在決定是否拋出異常,但是反射既然能執行私有化構造器那麼也能直接該變你的標記值,對於lazy的單例模式反射破壞單例是無法有效防禦的
public class Singleton {
private static Singleton instance;
private static boolean flag = true;
private Singleton() {
//在複雜的邏輯都是卵的
if (flag) {
flag = false;
} else {
throw new RuntimeException("單例構造器禁止反射調用");
}
}
public synchronized static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
//通過反射來破壞lazy類型的單例模式,是無法有效避免的
@Test
public void test() throws Exception {
Singleton instance = Singleton.getInstance(); //
Class<Singleton> objClass = Singleton.class;
Constructor<Singleton> constructor = objClass.getDeclaredConstructor();
constructor.setAccessible(true);
//修改字段的值
Field flag = objClass.getDeclaredField("flag");
flag.setAccessible(true);
flag.set(instance, true);
Singleton reflexObj = constructor.newInstance();
System.out.println(reflexObj); //Singleton@22927a81
System.out.println(instance); //Singleton@78e03bb5
System.out.println(reflexObj == instance); //false
}
7 完美的單例模式
7.1 枚舉能夠防止序列化破壞
public enum EnInstance {
//
INSTANCE;
private Object data;
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public static EnInstance getInstance() {
return INSTANCE;
}
}
@Test
public void test() throws IOException, ClassNotFoundException {
//反序列化
EnInstance instance = EnInstance.getInstance();
//
instance.setData(new Date());
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("singleton_file"));
oos.writeObject(instance);
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("singleton_file")));
EnInstance deserializationObj = (EnInstance) ois.readObject();
System.out.println(instance.getData()); // Fri May 22 11:15:50 CST 2020
System.out.println(deserializationObj.getData()); // Fri May 22 11:15:50 CST 2020
System.out.println(instance == deserializationObj);//true
}
//java.io.ObjectInputStream#readEnum
private Enum<?> readEnum(boolean unshared) throws IOException {
String name = readString(false);
Enum<?> result = null;
Class<?> cl = desc.forClass();
if (cl != null) {
try {
//拿到的是唯一的
Enum<?> en = Enum.valueOf((Class)cl, name);
7.2 枚舉能夠防止反射破壞
//java.lang.Enum
//枚舉只有唯一的構造器
//protected Enum(String name, int ordinal) {
// this.name = name;
// this.ordinal = ordinal;
//}
@Test
public void test() throws Exception {
Class<EnInstance> objClass = EnInstance.class;
Constructor<EnInstance> constructor = objClass.getDeclaredConstructor(); // 無法獲取到無參構造器 java.lang.NoSuchMethodException: EnInstance.<init>()
}
//java.lang.Enum
//枚舉只有唯一的構造器
//protected Enum(String name, int ordinal) {
// this.name = name;
// this.ordinal = ordinal;
//}
@Test
public void test() throws Exception {
Class<EnInstance> objClass = EnInstance.class;
Constructor<EnInstance> constructor = objClass.getDeclaredConstructor(String.class, int.class);
constructor.setAccessible(true);
EnInstance reflexObj = constructor.newInstance("喬碧蘿", 58); //java.lang.IllegalArgumentException: Cannot reflectively create enum objects
//java.lang.reflect.Constructor.newInstance
//public T newInstance(Object ... initargs) {
// //如果是枚舉是不允許反射創建對象的
// if ((clazz.getModifiers() & Modifier.ENUM) != 0)
// throw new IllegalArgumentException("Cannot reflectively create enum objects");
}
//jad EnInstance.class
public final class EnInstance extends Enum {
public static EnInstance[] values(){
return (EnInstance[])$VALUES.clone();
}
public static EnInstance valueOf(String name){
return (EnInstance)Enum.valueOf(EnInstance, name);
}
//構造器私有
private EnInstance(String s, int i){
super(s, i);
}
public Object getData(){
return data;
}
public void setData(Object data){
this.data = data;
}
public static EnInstance getInstance(){
return INSTANCE;
}
//靜態類變量
public static final EnInstance INSTANCE;
private Object data;
private static final EnInstance $VALUES[];
static
{
INSTANCE = new EnInstance("INSTANCE", 0);
$VALUES = (new EnInstance[] {
INSTANCE
});
}
}