Java設計模式之單例模式最佳實踐
//新建枚舉類
public enum EnumInstance {
INSTANCE;
private Object data;
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public static EnumInstance getInstance(){
return INSTANCE;
}
}
//調用
EnumInstance instance=EnumInstance.getInstance();
try {
//將對象寫入文件
ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("singleton_file"));
oos.writeObject(instance);
File file=new File("singleton_file");
//從文件中獲取對象
ObjectInputStream ois=new ObjectInputStream(new FileInputStream(file));
EnumInstance instance2= (EnumInstance) ois.readObject();
//比較兩個對象是否相同
System.out.println(instance);
System.out.println(instance2);
System.out.println(instance == instance2);
} catch (Exception e) { e.printStackTrace(); }
//結果
INSTANCE
INSTANCE
true
//結論:反序列化後的對象與之前的對象相同。
//再看一下data對象是否相同
EnumInstance instance=EnumInstance.getInstance();
//設置data
instance.setData(new Object());
try {
//將對象寫入文件
ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("singleton_file"));
oos.writeObject(instance);
File file=new File("singleton_file");
//從文件中獲取對象
ObjectInputStream ois=new ObjectInputStream(new FileInputStream(file));
EnumInstance instance2= (EnumInstance) ois.readObject();
//比較兩個對象是否相同
System.out.println(instance.getData());
System.out.println(instance2.getData());
System.out.println(instance.getData() == instance2.getData());
} catch (Exception e) { e.printStackTrace(); }
//結果
java.lang.Object@378bf509
java.lang.Object@378bf509
true
下面分析一下源碼,爲什麼枚舉類不會破壞單例?
EnumInstance instance2= (EnumInstance) ois.readObject();
private Object readObject0(boolean var1) throws IOException {
case 126:
var4 = this.checkResolve(this.readEnum(var1));
}
private Enum<?> readEnum(boolean var1) throws IOException{
String var5 = this.readString(false);
Enum var6 = null;
Class var7 = var2.forClass();
if (var7 != null) {
try {
//直接返回枚舉類裏面的對象,因此序列化不會破壞單例模式
Enum var8 = Enum.valueOf(var7, var5);
}
}
}
下面看看能否通過反射獲取構造器來實例化對象。
//首先通過單例模式獲取對象
EnumInstance instance=EnumInstance.getInstance();
try {
Class objectClass=EnumInstance.class;
//通過反射獲取構造器,這裏需要傳入參數
Constructor constructor=objectClass.getDeclaredConstructor(String.class,int.class);
//修改構造器的權限
constructor.setAccessible(true);
//通過構造器獲取新的對象,這裏需要傳入參數
EnumInstance newInstance= (EnumInstance) constructor.newInstance("crook",888);
//比較兩個對象是否相同
System.out.println(instance);
System.out.println(newInstance);
System.out.println(instance == newInstance );
} catch (Exception e) {
e.printStackTrace();
}
//結果:不能通過反射獲取構造器來實例化對象。
java.lang.IllegalArgumentException: Cannot reflectively create enum objects
at java.lang.reflect.Constructor.newInstance(Constructor.java:417)
at com.zk.javatest.singleton.lazy_singleton.Test.main(Test.java:88)
下面推薦一個反編譯工具jad,反編譯枚舉類到底發生了什麼?
jad下載地址:
下載完成後,解壓,然後將EnumInstance.class文件複製到解壓後的文件夾,如下圖:
在該文件夾下打開終端,執行命令:
jad EnumInstance.class
然後就會生產反編譯後的文件EnumInstance.jad,用vim或者sublime打開該文件。
//反編譯後的類
public final class EnumInstance extends Enum
{
public static EnumInstance[] values()
{
return (EnumInstance[])$VALUES.clone();
}
public static EnumInstance valueOf(String name)
{
return (EnumInstance)Enum.valueOf(com/zk/javatest/singleton/lazy_singleton/EnumInstance, name);
}
private EnumInstance(String s, int i)
{
super(s, i);
}
public Object getData()
{
return data;
}
public void setData(Object data)
{
this.data = data;
}
public static EnumInstance getInstance()
{
return INSTANCE;
}
public static final EnumInstance INSTANCE;
private Object data;
private static final EnumInstance $VALUES[];
static
{
INSTANCE = new EnumInstance("INSTANCE", 0);
$VALUES = (new EnumInstance[] {
INSTANCE
});
}
}