单例模式介绍
单例模式是应用最广泛的模式之一,也可能是很多刚入门的工程师唯一会使用的设计模式。在应用这个模式时,单例对象的类必须保证只有一个实例存在。许多时候整个系统只需要拥有一个全局对象,这样有利于我们协调系统的整体行为。
如:在一个应用中,应该只有一个ImageLoader实例,这个ImageLoader中又含有线程池、缓存系统、网络请求等,很消耗资源,因此,没有理由让他构造多个实例。这种不能自由构造对象的情况,就是单例模式的使用场景。
单例模式的简单示例
单例模式是设计模式中比较简单的,只有一个单例类,没有其他的层次结构与抽象。该模式要确保该类只能生成一个对象,通常是该类需要消耗较多的资源或者没有多个实例的情况。例如,一个公司只有一个CEO、一个应用只有一个Application对象等。下面以公司里的CEO为例来简单演示一下,一个公司可以有几个VP、无数个员工,但CEO只有一个。
饿汉单例模式
package com.example.xll.singleton;
/**
* 功能:普通员工类
* 作者:xll
* 日期:2016/11/17
* 邮箱:liangliang.xu1110@gmail.com
*/
public class Staff {
public void work(){
//干活
}
}
package com.example.xll.singleton;
/**
* 功能:副总裁
* 作者:xll
* 日期:2016/11/17
* 邮箱:liangliang.xu1110@gmail.com
*/
public class VP extends Staff {
@Override
public void work() {
//管理下面的经理
}
}
package com.example.xll.singleton;
/**
* 功能:CEO,饿汉单例模式
* 作者:xll
* 日期:2016/11/17
* 邮箱:liangliang.xu1110@gmail.com
*/
public class CEO extends Staff {
private static final CEO mCeo = new CEO();
//构造函数私有
private CEO(){
}
//公有的静态函数,对外暴露获取单例对象的接口
public static CEO getmCeo(){
return mCeo;
}
@Override
public void work() {
//管理VP
}
}
package com.example.xll.singleton;
import android.util.Log;
import java.util.ArrayList;
import java.util.List;
/**
* 功能:公司类
* 作者:xll
* 日期:2016/11/17
* 邮箱:liangliang.xu1110@gmail.com
*/
public class Company {
private List<Staff> mStaffList = new ArrayList<>();
public void addStaff(Staff person){
mStaffList.add(person);
}
public void showAllStaffs(){
for (Staff person : mStaffList) {
Log.i("员工:", person.toString());
}
}
}
package com.example.xll.singleton;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
/**
* 功能:测试
* 作者:xll
* 日期:2016/11/17
* 邮箱:liangliang.xu1110@gmail.com
*/
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Company cp = new Company();
//CEO对象只能通过getCeo函数获取
Staff ceo1 = CEO.getmCeo();
Staff ceo2 = CEO.getmCeo();
cp.addStaff(ceo1);
cp.addStaff(ceo2);
//通过new创建VP对象
Staff vp1 = new VP();
Staff vp2 = new VP();
//通过new创建Staff对象
Staff staff1 = new Staff();
Staff staff2 = new Staff();
Staff staff3 = new Staff();
cp.addStaff(vp1);
cp.addStaff(vp2);
cp.addStaff(staff1);
cp.addStaff(staff2);
cp.addStaff(staff3);
cp.showAllStaffs();
}
}
输出结果如下:
从上述代码中可以看到,CEO类不能通过new的形式构造对象,只能通过CEO.getmCeo()函数来获取,而CEO对象是静态对象,并且在声明的时候就已经初始化,这就保证了CEO对象的唯一性。从输出结果中发现,CEO两次输出的CEO对象都是一样的,而VP、Staff等类型的对象都是不同。这个实现的核心在于将CEO类的构造方法私有化,使得外部程序不能通过构造函数来构造CEO对象,而CEO对象通过一个静态方法返回一个静态对象。
懒汉单例模式
懒汉模式是声明一个静态对象,并且在用户第一次调用getInstance时进行初始化,而上述饿汉模式(CEO类)实在声明静态对象时就已经初始化。
package com.example.xll.singleton;
/**
* 功能:懒汉模式
* 作者:xll
* 日期:2016/11/17
* 邮箱:liangliang.xu1110@gmail.com
*/
public class Singleton {
private static Singleton instance;
private Singleton(){
}
public static synchronized Singleton getInstance(){
if(instance==null){
instance = new Singleton();
}
return instance;
}
}
大家可能已经发现了,getInstance()方法中添加了synchronized关键字,也就是getInstance()的一个同步方法,这是在多线程情况下保证单例对象唯一性的手段。细想一下,大家可能会发现一个问题,即使instance已经被初始化(第一次调用时会被初始化),每次调用getInstance方法都会进行同步,这样会消耗不必要的资源,这也是懒汉式单例模式存在的最大问题。
最后总结一下,懒汉单例模式的优点是单例只有在使用时才会被实例化,在一定程度上节约了资源;缺点是第一次加载时需要及时进行实例化,反应稍慢,最大的问题是每次调用getInstance都进行同步,造成不必要的同步开销。这种模式一般不建议使用。