在優銳課的學習分享中,我們探討了破壞單例屬性的三種主要方法以及如何防止它。分享給大家參考學習。
我們習慣于在需要時在我們的應用程序中使用單例設計模式。 眾所周知,在單例設計模式中,我們只能創建一個實例并在整個應用程序中訪問它。 但是在某些情況下,它將破壞單例行為。
在三個主要概念中,我們可以打破Java中Singleton類的singleton屬性。 在這篇文章中,我們將討論如何破壞它以及如何防止它。
這是示例Singleton類和SingletonTest類。
單例.
Singleton.Java
package demo1;
public final class Singleton {
private static volatile Singleton instance = null;
private Singleton() {
}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
SingletonTest.java
package demo1;
public class SingletonTest {
public static void main(String[] args) {
Singleton object1 = Singleton.getInstance();
Singleton object2 = Singleton.getInstance();
System.out.println("Hashcode of Object 1 - " + object1.hashCode());
System.out.println("Hashcode of Object 2 - " + object2.hashCode());
}
}
這是輸出; 你可以看到它具有與objectOne和objectTwo相同的hashcode :
Hashcode of Object 1 - 1836019240
Hashcode of Object 2 - 1836019240
現在,我們將打破這種模式。 首先,我們將使用Java反射。
反射
Java Reflection是一種API,用于在運行時檢查或修改方法,類和接口的行為。 使用Reflection API,我們可以在Singleton類中創建多個對象。 考慮以下示例:
ReflectionSingleton.java
package demo1;
import java.lang.reflect.Constructor;
public class ReflectionSingleton {
public static void main(String[] args) {
Singleton objOne = Singleton.getInstance();
Singleton objTwo = null;
try {
Constructor constructor = Singleton.class.getDeclaredConstructor();
constructor.setAccessible(true);
objTwo = (Singleton) constructor.newInstance();
} catch (Exception ex) {
System.out.println(ex);
}
System.out.println("Hashcode of Object 1 - "+objOne.hashCode());
System.out.println("Hashcode of Object 2 - "+objTwo.hashCode());
}
}
這個例子展示了反射如何用Java反射來打破單例模式。 你將獲得兩個哈希碼,如下所示。 它在單例模式上有所突破。
防止單例模式反射
有很多方法可以防止反射API中的Singleton模式,但是最好的解決方案之一是,如果實例已經存在,則在構造函數中引發運行時異常。 在這種情況下,我們無法創建第二個實例。
反序列化
在序列化中,我們可以將字節流的對象保存到文件中或通過網絡發送。 假設你先序列化Singleton類,然后再次對該對象反序列化,它將創建一個新實例,因此反序列化將破壞Singleton模式。
以下代碼用于說明單反模式如何隨反序列化而中斷。
為Singleton類實現Serializable接口。
DeserializationSingleton.Java
package demo1;
import java.io.*;
public class DeserializationSingleton {
public static void main(String[] args) throws Exception {
Singleton instanceOne = Singleton.getInstance();
ObjectOutput out = new ObjectOutputStream(new FileOutputStream("file.text"));
out.writeObject(instanceOne);
out.close();
ObjectInput in = new ObjectInputStream(new FileInputStream("file.text"));
Singleton instanceTwo = (Singleton) in.readObject();
in.close();
System.out.println("hashCode of instance 1 is - " + instanceOne.hashCode());
System.out.println("hashCode of instance 2 is - " + instanceTwo.hashCode());
}
}
輸出如下,你可以看到兩個 hashcodes.
hashCode of instance 1 is - 2125039532
hashCode of instance 2 is - 381259350
防止單例模式反序列化
為了克服這個問題,我們需要覆蓋Singleton類中的readResolve()方法并返回相同的Singleton實例。 使用以下方法更新Singleton.java。
protected Object readResolve() {
return instance;
}
現在,運行上面的DeserializationDemo類并查看輸出。
hashCode of instance 1 is - 2125039532
hashCode of instance 2 is - 2125039532
克隆
使用“克隆”方法,我們可以創建原始對象的副本; 如果我們在單例模式中應用克隆,這是同一回事。 它將創建兩個實例:一個實例和另一個實例。 在這種情況下,我們將打破Singleton原理,如下面的代碼所示。
實施「可克隆」介面,并在上述Singleton類別中覆寫clone方法
Singleton.java
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
然后,測試克隆以打破單例。
CloningSingleton.java
public class CloningSingleton {
public static void main(String[] args) throws CloneNotSupportedException, Exception {
Singleton instanceOne = Singleton.getInstance();
Singleton instanceTwo = (Singleton) instanceOne.clone();
System.out.println("hashCode of instance 1 - " + instanceOne.hashCode());
System.out.println("hashCode of instance 2 - " + instanceTwo.hashCode());
}
}
這是輸出:
hashCode of instance 1 - 1836019240
hashCode of instance 2 - 325040804
如果我們看到上面的輸出,則兩個實例具有不同的hashcodes。 這意味著這些實例不相同。
防止單例模式克隆
在上面的代碼中,它打破了Singleton原理,即。 e創建了兩個實例。 為了克服上述問題,我們需要實現/覆蓋clone()方法并從克隆方法中拋出異常CloneNotSupportedException。 如果有人嘗試創建Singleton的克隆對象,它將拋出異常,如以下代碼所示。
@Override
protected Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException();
}
現在,我們可以運行loningSingleton類; 在創建單個對象的克隆對象時,它將拋出CloneNotSupportedException。
文章寫到這里,如有不足之處,歡迎補充評論.希望這篇文章對你有用!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。