Item3 Enforce the singleton property with a private constructor or an enum type
使用私有構造方法或者枚舉類型實現單例。
單例(Singleton)是指只實例化一次的類。
單例表示本質上唯一的系統組件,例如文件系統或者窗口管理器。
package com.googlecode.javatips4u.effectivejava.singleton;
public class StaticFinalFieldSingleton {
public static final StaticFinalFieldSingleton INSTANCE = new StaticFinalFieldSingleton();
private StaticFinalFieldSingleton() {
}
}
當類StaticFinalFieldSingleton類被實例化時,有且僅有一個StaticFinalFieldSingleton實例存在。
(除非客戶端通過類反射機制調用該私有的構造方法,否則外部無法改變單例)
package com.googlecode.javatips4u.effectivejava.singleton;
public class StaticFactoryMethodSingleton {
private static final StaticFactoryMethodSingleton INSTANCE = new StaticFactoryMethodSingleton();
private StaticFactoryMethodSingleton() {
}
public static StaticFactoryMethodSingleton getInstance() {
return INSTANCE;
}
}
public static final成員的優點在於客戶端很容易分辨是單例。static(整個類公用的)final(不能改變的)確保實例是相同對象的引用。
public static method的優點在於增加了靈活性,可以修改方法內部而不引起客戶端API的改變,例如每個線程一個單例。另外一個有點在於涉及到泛型的時候(Item27)。
Item27:
當對單例類進行Serializable處理時,僅僅實現Serializable接口還是不夠的,確切的說,如果只是實現Serializable接口,那麼被readObject讀取的實例並不是writeObject時存儲的類。
需要將所有的實例成員設置爲transient(why???),然後實現readResolve()方法:
ObjectInputStream.readObject() -> ObjectInputStream.readObject0(boolean) -> ... -> Method.invoke() -> ... SerializableSingleton.readResolve()
例如在下面的例子中,如果不實現readResolve()方法,那麼write和read的object是不同的實例。
package com.googlecode.javatips4u.effectivejava.singleton;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class SerializableSingleton implements Serializable {
private String mDesc = "I am transient";
private static final long serialVersionUID = 1L;
private static final SerializableSingleton INSTANCE = new SerializableSingleton();
private SerializableSingleton() {
}
public static SerializableSingleton getInstance() {
return INSTANCE;
}
public String getDesc() {
return mDesc;
}
public void setDesc(String desc) {
this.mDesc = desc;
}
// readResolve method to preserve singleton property
public Object readResolve() {
return INSTANCE;
}
public static void main(String[] args) {
File file = new File("singleton");
ObjectOutputStream oos = null;
ObjectInputStream ois = null;
try {
oos = new ObjectOutputStream(new FileOutputStream(file));
SerializableSingleton instance = SerializableSingleton
.getInstance();
instance.setDesc("I changed you!");
System.out.println(instance);
oos.writeObject(instance);
oos.close();
ois = new ObjectInputStream(new FileInputStream(file));
SerializableSingleton instance2 = (SerializableSingleton) ois
.readObject();
System.out.println(instance2);
System.out.println(instance2.getDesc());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
if (oos != null) {
try {
oos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (ois != null) {
try {
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
在Java1.5中,還有另外一種方法來實現單例,那就是枚舉類型:a single-element enum type is the best way to implement a singleton.單元素的枚舉類型是實現單例的最好方式。
package com.googlecode.javatips4u.effectivejava.singleton;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public enum EnumSingleton implements Serializable {
INSTANCE;
private String book = null;
public String getBook() {
return book;
}
public void setBook(String book) {
this.book = book;
}
public void doSomething() {
};
}
class EnumSingletonSerialization {
public static void main(String[] args) {
File file = new File("singleton");
ObjectOutputStream oos = null;
ObjectInputStream ois = null;
try {
oos = new ObjectOutputStream(new FileOutputStream(file));
EnumSingleton singleton = EnumSingleton.INSTANCE;
singleton.setBook("HelloWorld");
oos.writeObject(EnumSingleton.INSTANCE);
System.out.println(EnumSingleton.INSTANCE);
oos.close();
ois = new ObjectInputStream(new FileInputStream(file));
EnumSingleton singleton2 = (EnumSingleton) ois.readObject();
System.out.println(singleton2.getBook());
System.out.println(singleton == singleton2);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
if (oos != null) {
try {
oos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (ois != null) {
try {
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}