有时候,我们希望某一个类只能产生一个对象。比如说一个学校的校长只能有一个。
要一个类只能有一个实例,也就是说我们要把它的构造器隐藏起来:private Something(){}
然后提供一个对外的方法,用来实际的产生/返回对象。也就是说我们可以得到下边的实现:
public class Something {
private static Something INSTANCE = null;
private Something(){}
public static Something getINSTANCE() {
if (INSTANCE == null){
INSTANCE = new Something();
}
return INSTANCE;
}
}
这是一个单例模式的简单实现。我们发现,每次getINSTANCE()
的时候,都要判断INSTANCE == null
。
所以我们也可以这样(如果你的单例对象足够小,而且运行期间都需要):
public class Something {
private static Something INSTANCE = new Something();
private Something(){}
public static Something getINSTANCE() {
return INSTANCE;
}
}
上边的单例模式实现在多线程下是不安全的。因为不能保证INSTANCE
的值不会改变。解决线程问题的方法,很简单,加锁。
比如,我们给getINSTANCE
方法加锁。:
public static synchronized Something getINSTANCE() {
if (INSTANCE == null){
INSTANCE = new Something();
}
return INSTANCE;
}
或者在判断null的时候加锁:
public static Something getINSTANCE() {
synchronized (Something.class){
if (INSTANCE == null){
INSTANCE = new Something();
}
}
return INSTANCE;
}
但是,这样有个问题,就是每次获取实例的时候,都有锁操作,会耗费资源,假如,这个方法被频繁使用,会有性能问题。
把代码改一下:
public static Something getINSTANCE() {
if (INSTANCE == null){
synchronized (Something.class){
if (INSTANCE == null){
INSTANCE = new Something();
}
}
}
return INSTANCE;
}
这样写的好处是:只有在INSTANCE
为null的时候才会加锁处理。也就是所只起作用一次。
public enum Singleton {
INSTANCE;
public void whateverMethod() {
}
}
使用枚举来实现单例,在类真正被用到的时候才会加载对象(利用类加载机制),加载过程是同步的,是线程安全的。
前边的单例在面对序列化、反序列化的时候还是不安全的,但是对于枚举,直接规定不能反序列化枚举对象。。。
public abstract class Enum<E extends Enum<E>> implements Comparable<E>, Serializable {
// 略
// 不允许反序列化枚举对象
private void readObject(ObjectInputStream in) throws IOException,
ClassNotFoundException {
throw new InvalidObjectException("can't deserialize enum");
}
// 不允许反序列化枚举对象
private void readObjectNoData() throws ObjectStreamException {
throw new InvalidObjectException("can't deserialize enum");
}
// 枚举类不可以有finalize方法,子类不可以重写该方法 保证实例的对象唯一
protected final void finalize() { }
}