所谓Singleton,是指仅能被实例化一次的类。Singleton通常代表本质上唯一的系统组件,例如窗口管理器或文件系统。让一个类成为Singleton就无法为Singleton替换模拟实现,除非它实现一个作为其类型的接口,所以会让其客户端难于测试。
JDK1.5之前,有两种方法可以实现Singleton,二者均基于让构造函数私有化,并导出一个公共静态成员来提供对唯一实例的访问。在方法一中,该公共静态成员是一个final域:
//Singleton with public final field
public class Elvis{
public static final Elvis INSTANCE = new Elvis();
private Elvis(){...}
public void leaveTheBuilding(){...}
}
这里私有的构造函数仅被调用一次,用来初始化公共静态final域Elvis.INSTANCE。由于未提供public或protected的构造函数,所以保证了Elvis的全局唯一:一旦Elvis类被实例化,就只会存在唯一的一个Elvis实例,不多也不少。客户端的任何行为都改变不了这一点,
不过要提醒一点:享有特权的客户端可以借助AccessibleObject.setAccessible()方法,通过反射(Item53)调用private构造函数。如果你需要防止这种攻击,则修改构造函数,当它被调用来创建第二个实例时抛出一个异常。
在实现Singleton的方法二中,该公共静态成员是一个静态工厂方法:
//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实例。(上文的提醒也要关注)。
public域方法(方法一)的主要优点是,类声明清楚地表明该类是Singleton:公共的静态域是final的,所以该域总是包含相同的对象引用。public域方法的性能优势已不存在了:现代的java虚拟机实现差不多都将其调用内联到静态工厂方法。
工厂方法(方法二)的一个优点是,你可以灵活地决定是否将类设为Singleton,而不用改变其API。例如,返回唯一实例的工厂方法,可以很容易地改为为每个调用它的线程返回一个唯一实例。第二个优点和Item27所讨论的泛型有关。通常这些优势都不相关,而且静态域方法更简单。
为了将使用上述方法的Singleton类变为可序列化,单单增加implements Serializable是不够的。为了维持Singleton的保证,你还需要将所有实例变量声明为transient,并提供readResolve方法*(Item77)。否则,每次实例反序列化时,都会创建一个新的实例。在上例中就会导致“假冒的Elvis”,为了防止这种情况,需要在Elvis类中增加readResolve方法:
//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;
}
JDK1.5之后,还有
第三种方法来实现Singleton,只要简单地编写一个只包含一个元素的枚举类型:
//Enum singletion - the preferred approach
public enum Elvis{
INSTANCE;
public void leaveTheBuilding(){...}
}
该方法与公共域方法类似,不过它更加简洁,无偿提供序列化机制,并且绝对保证不会被多次实例化,即使是在面对复杂的序列化或反射攻击的时候也这样。虽然这种方法尚未被广泛采用,但
单元素的枚举类型是实现Singleton的最佳方式。
#######################################
关于readResolve():
JDK API描述:
将对象写入流时需要指定要使用的替代对象的可序列化类,应使用准确的签名来实现此特殊方法:
ANY-ACCESS-MODIFIER Object writeReplace() throws ObjectStreamException;
在从流中读取类的一个实例时需要指定替代的类,应使用的准确签名来实现此特殊方法。
ANY-ACCESS-MODIFIER Object readResolve() throws ObjectStreamException;
此 writeReplace、readResolve 方法将由序列化调用,前提是如果此方法存在,而且它可以通过被序列化对象的类中定义的一个方法访问。因此,该方法可以拥有私有 (private)、受保护的 (protected) 和包私有 (package-private) 访问。子类对此方法的访问遵循 java 访问规则。
也就是说,
writeReplace()方法返回的对象,就是要被序列化的对象,我们有机会在序列化前把这个对象给换成我们确定好的那个(貌似没什么用处);
readResolve()方法就是在反序列化完成得到对象前,把这个对象给换成我们确定好的那个。
为了防止有人恶意通过序列化的机制破坏定义好的单例,就需要自己实现readResolve()方法,把单例定义的唯一实现在这个方法中返回。
分享到:
相关推荐
将构造函数和析构函数声明为私有成员,就意味着外部用户不能直接调用这些函数,从而控制对象的创建和销毁。这种方式通常用于实现 Singleton 模式或禁止对象的复制。 1. 将构造函数和析构函数声明为保护成员 将构造...
在C++中实现Singleton模式的基本思路是通过私有构造函数和静态成员函数来控制实例的创建过程。示例中的代码展示了这种实现方式: ```cpp class Singleton { public: static Singleton& Instance() { if (_...
4. **私有构造函数与赋值运算符**:Singleton的构造函数和赋值运算符都声明为私有的,防止外部代码直接创建或复制Singleton对象,确保只有一个实例存在。 5. **模板化实现**:为了减少代码重复,可以使用模板类来...
Singleton() {} // 构造函数私有化,防止直接创建实例 ~Singleton() {} // 析构函数私有化,防止直接删除 void initSingleton() { instance = std::make_shared<Singleton>(); } static std::once_flag init...
首先,Singleton模式的核心在于控制类的实例化过程,通常通过私有构造函数和静态成员函数来实现。`Singleton.cpp`和`Singleton.h`两个文件中可能包含了以下内容: 在`Singleton.h`中,我们定义Singleton类: ```...
Singleton() {} // 私有构造函数 public: static Singleton* getInstance() { if (instance == nullptr) { instance = new Singleton(); } return instance; } ~Singleton() { delete instance; } // 析构...
它通过将单例类的构造函数设置为私有,并在内部类中创建一个静态的单例对象,保证了只有一个实例存在。例如: ```java public class Singleton { private static class SingletonHolder { private static final ...
Singleton() {} // 私有化构造函数 static Singleton* instance; public: static Singleton* getInstance() { return instance; } }; Singleton* Singleton::instance = new Singleton(); ``` 3. **双检...
在 Java 中实现 Singleton 属性有两种方式,一种是使用私有构造器强化 Singleton 属性,另一种是使用枚举强化 Singleton 属性。对于第一种方法,可以使用私有构造器来实例化公有的静态 final 域,然后使用静态方法 ...
Singleton() {} // 私有构造函数 static std::once_flag init_flag; static Singleton* instance; public: static Singleton& getInstance() { std::call_once(init_flag, &Singleton::createInstance); ...
单例可以通过私有构造器和静态工厂方法实现,或者使用枚举类型。使用枚举类型实现单例可以确保线程安全且防止反射攻击,是推荐的实现方式: ```java public enum Singleton { INSTANCE; public void someService...
Singleton拥有一个私有构造函数,确保用户无法通过new直接实例化它。除此之外,该模式中包含一个静态私有成员变量instance与静态公有方法Instance()。Instance()方法负责检验并实例化自己,然后存储在静态成员变量中...
通常,C++中的单例实现可能会涉及静态成员、私有构造函数、线程安全考虑以及懒汉式(延迟初始化)或饿汉式(提前初始化)的实现策略。 在压缩包中的文件名“single2.cpp”和“single.cpp”可能分别展示了两种不同的...
通过一个静态工厂函数返回单例对象,这种方式可以隐藏单例的实现细节,但可能不适用于需要类型擦除或模板的情况。 在实际应用中,选择哪种单例模式取决于项目需求,如是否需要线程安全、资源管理策略、延迟初始化...
Singleton模式通常包含三个关键部分:私有化构造函数以防止外部直接实例化,一个静态成员变量用于存储唯一的实例,以及一个公共的静态方法(通常命名为getInstance)供外界获取这个唯一实例。 在C++中,Singleton的...
传统的C++单例实现可能使用静态成员变量和私有构造函数来达到这一目的: ```cpp class Singleton { private: Singleton() {} // 私有构造函数 static Singleton* instance; // 静态成员变量 public: static ...
Singleton() {} // 私有构造函数防止外部实例化 Singleton(const Singleton&) = delete; // 禁止拷贝构造 Singleton& operator=(const Singleton&) = delete; // 禁止赋值操作 }; ``` 这种方法简洁且线程安全...
2. **使用静态工厂方法**:如果一个类既提供了静态工厂方法也提供了构造器,则通常推荐使用静态工厂方法来创建对象,以减少不必要的对象创建。 通过以上讨论可以看出,使用静态工厂方法不仅可以让代码更加简洁易懂...
1. 封锁构造函数:首先,将类的构造函数设为私有(private),防止外部代码直接创建实例。 ```cpp class Singleton { private: Singleton() {} }; ``` 2. 创建静态成员变量:在类内部创建一个指向该类类型的静态指针...