美文网首页
第3项 使用私有构造方法或者枚举来实现单例

第3项 使用私有构造方法或者枚举来实现单例

作者: Cocoonshu粽子 | 来源:发表于2018-12-26 22:39 被阅读9次

    单例即为只初始化一次的类,我们可以一贯性的认为,单例代表系统唯一的系统组件,就像是window manager(窗口管理器)或者file system(文件系统)。

    我们有两种方法实现单例。但是两种方法我们都需要在这个待实现的单例中提供一个私有的构造方法和一个共有静态的方法来处理这个唯一的实例。在其中的一个实现中,我么希望类的成员变量是final修饰的。

        // Singleton with public final field
        public class Elvis {
            public static final Elvis INSTANCE = new Elvis();
            private Elvis() { ... }
            public void leaveTheBuilding() { ... }
        }
    

    私有构造函数只调用一次,以初始化公共静态最终字段Elvis.INSTANCE。 缺乏公共或受保护的构造函数保证了“单一的”宇宙:一旦Elvis类被初始化,就会存在一个Elvisinstance - 不多也不少。 客户端所做的任何事情都无法改变这一点,但有一点需要注意:特权客户端可以借助AccessibleObject.setAccessible方法反射性地调用私有构造函数(第65项)。 如果您需要防御此攻击,请修改构造函数以使其在要求创建第二个实例时抛出异常。 在实现单例的第二种方法中,公共成员是一种静态工厂方法:

        // Singleton with static factory
        public class Elvis {
            private static final Elvis INSTANCE = new Elvis();
            private Elvis() { ... }
            public static Elvis getInstance() { return INSTANCE; }
            public void leaveTheBuilding() { ... }
        }
    

    对Elvis.getInstance的所有调用都返回相同的对象引用,并且不会创建其他任何Elvis实例(前面提到过相同的警告)。
    公共字段方法的主要优点是API清楚地表明该类是单例:公共静态字段是final,因此它将始终包含相同的对象引用。 第二个优点是它更简单。

    静态工厂方法的一个优点是,它使您可以灵活地改变主意,关于类是否是单例而不更改其API。 工厂方法返回唯一的实例,但可以修改它,例如,为每个调用它的线程返回一个单独的实例。 第二个优点是,如果您的应用需要,您可以编写通用的单件工厂(第30项)。 使用静态工厂的最后一个优点是方法引用可以用作供应商,例如Elvis :: instance是Supplier <Elvis>。 除非其中一个优点相关,否则公共领域方法更可取。

    要创建一个使用这些方法中的任何一种可序列化的单例类(第12章),仅仅将实现Serializable添加到其声明中是不够的。 要维护单例保证,请声明所有实例字段为瞬态并提供readResolve方法(第89项)。 否则,每次反序列化序列化实例时,都会创建一个新实例,在我们的示例中,将导致虚假的猫王目击。 要防止这种情况发生,请将此readResolvemethod添加到Elvis类:

        // readResolve method to preserve singleton property
        private Object readResolve() {
    // Return the one true Elvis and let the garbage collector
    // take care of the Elvis impersonator. return INSTANCE;
        }
    

    实现单例的第三种方法是声明单元素枚举:

        // Enum singleton - the preferred approach
        public enum Elvis {
            INSTANCE;
            public void leaveTheBuilding() { ... }
        }
    

    这种方法类似于公共领域方法,但它更简洁,免费提供序列化机制,并提供了对多个实例化的铁定保证,即使面对复杂的序列化或反射攻击。 这种方法可能会有点不自然,但单元素枚举类型通常是实现单例的最佳方法。 请注意,如果您的单例必须扩展Enum以外的超类,则不能使用此方法(尽管您可以声明枚举来实现接口)。

    相关文章

      网友评论

          本文标题:第3项 使用私有构造方法或者枚举来实现单例

          本文链接:https://www.haomeiwen.com/subject/nnsmmftx.html