`

用私有构造器或者枚举类型强化Singleton属性

阅读更多

在1.5版本之前可以两种实现Singleton的方法,但是都要把构造器保存为私有的。

1、公有静态成员是个final域

 

 
  1. // Singleton with public final field - Page 17  
  2. public class Elvis {  
  3.     public static final Elvis INSTANCE = new Elvis();  
  4.     private Elvis() { }  
  5.   
  6.     public void leaveTheBuilding() {  
  7.         System.out.println("Whoa baby, I'm outta here!");  
  8.     }  
  9.   
  10.     // This code would normally appear outside the class!  
  11.     public static void main(String[] args) {  
  12.         Elvis elvis = Elvis.INSTANCE;  
  13.         elvis.leaveTheBuilding();  
  14.     }  
  15. }  

 

 

问题:但是客户端可以通过AccessibleObject.setAccessible方法,通过反射机制调用私有构造器。
AccessibleObject 类是 Field、Method 和 Constructor 对象的基类。它提供了将反射的对象标记为在使用时取消默认 Java 语言访问控制检查的能力。对于公共成员、默认(打包)访问成员、受保护成员和私有成员,在分别使用 Field、Method 或 Constructor 对象来设置或获得字段、调用方法,或者创建和初始化类的新实例的时候,会执行访问检查。 
在反射对象中设置 accessible 标志允许具有足够特权的复杂应用程序(比如 Java Object Serialization 或其他持久性机制)以某种通常禁止使用的方式来操作对象。 

2、公有的成员是个静态工厂方法
这种方法很清晰的表明了这个类是一个Singleton

 

 
  1. // Singleton with static factory - Page 17  
  2.   
  3. public class Elvis {  
  4.     private static final Elvis INSTANCE = new Elvis();  
  5.     private Elvis() { }  
  6.     public static Elvis getInstance() { return INSTANCE; }  
  7.   
  8.     public void leaveTheBuilding() {  
  9.         System.out.println("Whoa baby, I'm outta here!");  
  10.     }  
  11.   
  12.     // This code would normally appear outside the class!  
  13.     public static void main(String[] args) {  
  14.         Elvis elvis = Elvis.getInstance();  
  15.         elvis.leaveTheBuilding();  
  16.     }  
  17. }  

 

 

 

问题:同上

3、实现Singleton类变成可序列化的,仅仅实现序列化是不够的,为了维护并保证Singleton,必须声明所有实例域都是瞬时(transient)的,并提供一个readResolve方法。否则,每次反序列化一个序列的实例时,都会创建一个新的实例。
《The readResolve Method -- 序列化实现readResolve方法的作用》http://blog.csdn.net/partner4java/article/details/7058741

 

 

 
  1. import java.io.Serializable;  
  2.   
  3. // Serializable singleton with public final field - Page 18  
  4. public class Elvis implements Serializable{  
  5.     public static final Elvis INSTANCE = new Elvis();  
  6.     private Elvis() { }  
  7.   
  8.     public void leaveTheBuilding() {  
  9.         System.out.println("Whoa baby, I'm outta here!");  
  10.     }  
  11.   
  12.     private Object readResolve() {  
  13.         // Return the one true Elvis and let the garbage collector  
  14.         // take care of the Elvis impersonator.  
  15.         return INSTANCE;  
  16.     }  
  17.   
  18.     // This code would normally appear outside the class!  
  19.     public static void main(String[] args) {  
  20.         Elvis elvis = Elvis.INSTANCE;  
  21.         elvis.leaveTheBuilding();  
  22.     }  
  23. }  

 

 

 

4、从JDK 1.5开始,实现Singleton,可以编写一个包含单个元素的枚举类型。
而且可以解决复杂的反序列化或者反射的攻击。
单元素的枚举类型已经成为实现Singleton的最佳方法。

 

 
  1. // Enum singleton - the preferred approach - page 18  
  2. public enum Elvis {  
  3.     INSTANCE;  
  4.   
  5.     public void leaveTheBuilding() {  
  6.         System.out.println("Whoa baby, I'm outta here!");  
  7.     }  
  8.   
  9.     // This code would normally appear outside the class!  
  10.     public static void main(String[] args) {  
  11.         Elvis elvis = Elvis.INSTANCE;  
  12.         elvis.leaveTheBuilding();  
  13.     }  
  14. }  
分享到:
评论

相关推荐

    Effective-Java读书笔记(上)

    - **副作用**:使用私有构造器的一个副作用是使得该类不能被继承。 **避免创建不必要的对象**: 1. **不可变对象**:对于不可变对象,可以通过缓存已创建的对象来避免重复创建。 2. **使用静态工厂方法**:如果一...

    Java常用设计模式(SingleTon、FactoryMethod、AbstractFactory)

    实现单例时,通常会使用私有构造器防止外部创建实例,同时提供一个静态方法用于获取唯一的实例。Java中常使用双重检查锁定(Double-Check Locking)或枚举方式来实现线程安全的单例。 ```java // 双重检查锁定示例 ...

    现代编程语言- Kotlin 之美 - 当下最火的编程语言欣赏.pdf

    #### EJ 第3条:用私有构造器或枚举类型强化Singleton属性 单例模式是一种常用的软件设计模式,旨在确保一个类只有一个实例,并提供一个全局访问点。Kotlin通过`object`关键字进一步简化了单例模式的实现,使得创建...

    如果我将构造函数声明为私有,可以在另一个类中调用此构造函数

    另外,如果我们确实需要在不同类中使用私有构造函数,可以使用工厂方法或者友元类(friend class,Java中没有直接支持这一概念,但可以通过内部类或者持有对方的引用来实现类似效果)。工厂方法是一种设计模式,它...

    单例模式(Singleton)的6种实现

    Java中的枚举类型是线程安全的,并且只会装载一次,设计者充分考虑到了线程安全问题,因此使用枚举实现单例模式是一种简洁而且高效的解决方案。 6. 容器式单例(Singleton Holder) 通过一个私有的静态内部类...

    C#面向对象设计模式纵横谈(2):Singleton 单件(创建型模式)

    1. **构造器的访问控制**:将构造器设为私有或受保护的,防止外部直接通过`new`关键字创建实例。 2. **静态实例成员**:定义一个静态的类成员变量,用于存储Singleton实例。 3. **公共访问方法**:提供一个公共的...

    Java练习reflect,singleton,DomAndSax

    而“singleton method.txt”很可能是讲解或展示了不同类型的单例实现方式,包括传统的单例模式和基于枚举的单例实现。 通过深入学习和实践这三个主题,你将能更好地理解和掌握Java的核心特性,以及在实际项目中如何...

    JavaSE 面试题 (2).docx

    - 构造器私有化,防止其他类通过new关键字创建实例。 - 创建并保存该类的唯一实例,通常使用静态变量存储。 - 提供一个公共的静态方法或属性,以便外部获取这个唯一的实例。 常见的单例实现方式有以下几种: - **...

    java面向对象编程单实例模式解析

    ### Java面向对象编程中的单实例(Singleton)模式解析 #### 概述 在Java面向对象编程中,单...此外,随着Java语言的发展,还可以利用枚举类型来实现单实例模式,这种方式既简单又安全,值得在实际开发中考虑使用。

    单例设计模式个人总结+摘录

    1. **避免反射攻击**:在私有构造器中添加条件判断,如`if (instance != null)`,这样即便反射尝试创建新实例也会被阻止。 2. **避免反序列化攻击**:为单例类实现`readResolve()`方法,确保即使反序列化也不会创建...

    单态模式的设计和应用

    单态模式(Singleton Pattern)是一种常用的软件设计模式,它的核心目标是确保一个类在整个应用程序中只有一个实例,并提供全局...在某些情况下,如依赖注入框架或使用枚举类型替代,可以避免单态模式带来的复杂性。

    单例模式.doc

    - **枚举方式**:利用枚举类型天然的线程安全性和唯一性实现单例模式,简洁高效。 #### 五、单例模式的适用场景 单例模式适用于以下情况: - 系统只需要一个实例对象时。 - 单例对象需要被共享,并且所有的实例...

    设计模式_创建型_单例模式.md

    Java的反射API提供了获取构造器(Constructor)并创建对象的能力,如果构造器允许被访问,那么即使是私有的构造器也可以通过反射得到。因此,即使实现了单例模式,反射仍可能导致破坏单例模式的实例唯一性。在单例的...

    学习PMD软件中的札记

    在Java中,单例模式通常通过私有构造器、静态工厂方法或者枚举类型来实现。PMD的这个规则旨在提醒开发者,如果一个类只有一个公共构造器并且没有被标记为final,那么它可能是适合作为单例的。 在描述中提到的`...

    Java中控制创建对象的个数

    1. **私有构造器(Private Constructor)**:这是最常用的控制对象创建的方式,特别是用于实现单例模式。通过将类的构造器声明为私有,外部类无法直接实例化它。例如: ```java public class Singleton { private ...

    单例模式简介和java代码实现

    实现代码:public class Singleton { private Singleton() { // 私有化构造函数 } private static class SingletonHolder { private static final Singleton INSTANCE = new Singleton(); } public static Singleton...

    Java单例模式

    1. **私有构造器**: 单例类的构造器通常被声明为`private`,这样其他类无法通过`new`关键字直接创建该类的实例。这是防止外部代码实例化单例对象的第一道防线。 2. **静态内部类**: 在上述例子中,单例对象`...

    JAVA创建和销毁对象的方法

    单例可以通过私有构造器和静态工厂方法实现,或者使用枚举类型。使用枚举类型实现单例可以确保线程安全且防止反射攻击,是推荐的实现方式: ```java public enum Singleton { INSTANCE; public void someService...

Global site tag (gtag.js) - Google Analytics