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