曾经看过大话的设计模式,对单例模式有了一个初步的印象.那时候对单例的理解是对类进行计划生育.基本的代码也能够掌握,但是为什么要那么写?在实际的项目中如何应用?这些都是看完那本书之后留下的疑问,接下来通过学到的知识将单例模式进行步步的解析.不仅要了解它的诞生背后的故事,更要去学会在具体项目中如何运用.接下来将通过问题驱动的方式对单例模式进行步步剖析.
什么是单例模式?
用于确保一个类在系统中有一个实例.并负责自行实例化和向整个系统提供该实例的访问.
为什么要使用单例模式?
在面向对象编程中有这么两种情况:
1 在大部分情况下,我们程序员会把类的构造器定义成public访问权限,这样的话就可以循序任何类来创建该类的对象.
2 在一些特殊的时候,允许其他类来自由的创建该类没有任何意义,甚至是带来一些负面的影响,毕竟创建一个对象会带来相关的成本,系统开销.例如一个系统可能只会有一个窗口管理器,有再多的窗口管理器也是多余的.
所以我们需要一个类只能创建一个实例,并且此类提供一个访问它的公共的访问点.这便是单例模式.
如何使用单例模式?
单例模式中有以下三个特点:
- 静态的私有变量.
- 私有的构造方法.
- 公共的静态入口方法.
如果理解上面的三个特点,那么整个单例模式的框架就描绘出来了.
那为什么单例模式会有上面三个特点?
第一个特点:静态私有变量.因为该类必须缓存已经创建的对象,否则的话该类根本不知道是否创建过对象,也就无法保证只能创建一个对象,所以此类需要使用一个属性来保存曾经创建过的对象,而此属性需要被此类中的一个静态方法访问,所以这个属性必须使用static来进行修饰.
第二个特点:私有的构造方法.因为单例模式要求此类不能够被其他类创建对象,而只能允许该类创建一个对象,所以为了避免其他类来自由的创建此类的实例,我们就通过把该类的构造器使用private来修饰,从而将该类的构造器隐藏起来,其他类便不能创建该类的对象.
第三个特点:公共的静态入口方法.根据良好的封装原则,一旦该类的构造器被隐藏,则需要提供一个public方法作为该类的访问点,用于创建该类的对象,且方法必须使用static进行修饰,为什么呢,因为你在调用这个方法的时候不存在该类的对象,也就是说调用该方法的不可能是对象,只能是类,所用需要用static进行修饰.
以上的三个特点便是单例模式的核心点.下面则通过单例模式的代码来进行展示整个单例模式的过程:
单例类:Singleton
/**
* 简单的单例模式
* @author jnqqls
*
*/
publicclass Singleton {
//使用静态变量来缓存创建的实例.
private static Singleton instance = null;
//用private,隐藏构造器.
privateSingleton(){
}
//提供静态方法,用来返回Singleton实例.
//加入控制,保证只有一个实例.
publicstatic Singleton getInstance(){
if(instance == null){
//创建一个Singleton对象,并保存在instance中.
instance= new Singleton();
}
returninstance;
}
}
客户端:TestSingleton
/**
* 客户端程序,查看单例模式的效果.
* @author jnqqls
*
*/
publicclass TestSingleton {
/**
* @param args
*/
publicstatic void main(String[] args) {
//实例化第一个单例对象
SingletoninstanceOne = Singleton.getInstance();
//实例化第二个单例对象
SingletoninstanceTwo = Singleton.getInstance();
//查看两个实例是否是同一个对象?
System.out.println(instanceOne.equals(instanceOne));
System.out.println(instanceOne==instanceTwo);
}
}
输出结果:
true
true
通过上面的代码可以看出我们可以通过方法进行自定义控制,保证Singleton类只有一个实例,所以从最后的运行结果可以看到在TestSingleton类的main方法中所产生的两个对象其实是同一个对象.
那么在什么场景下适合用单例模式呢?
如果这个类中没有可以修改的成员变量,也就是说这个类没有状态,如果存在可以修改成员变量的话就会产生线程安全的问题,所以不建议使用.因为单例模式无法继承,所以也没有办法扩展,无法扩展其实现.比较适合单例模式是在读取配置文件的场景.
在接下来的文章将会介绍如何通过单例模式来读取配置文件.