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();
}
}
}
}
}