走穿java设计模式23种-1单例模式详解
java 23种设计模式介绍:http://blog.csdn.net/wenzhi20102321/article/details/54601909
单例模式的理解:
单例模式算是java设计模式中最简单的一个了。
别人问你对单例模式的理解,你可以这样说:
单例模式确保一个类只能有一个实例,不论再任何类内调用这个类的对象,都是调用同一个对象,并且这个类不能new出来,因为构造方法已经私有化了,只能用静态方法调用这个对象。
单例模式的作用:确保整个程序调用的对象是一致的,里面的某个数据是全局的,无论在哪一个页面改变数据,其他任何页面都是获取到这个最新的数据。
下面开始全面介绍一下,单例模式:
单例模式概念:
Java中单例模式是一种常见的设计模式,单例模式的写法有好几种,这里主要介绍三种:懒汉式单例、饿汉式单例、登记式单例(很少用)。
单例模式有以下特点:
1.单例类只能有一个实例。
2.单例类必须自己创建自己的唯一实例。
3.单例类必须给所有其他对象提供这一实例。
懒汉式单例模式:
懒汉式,说明这个类是很懒的,刚开始什么也不做,只有第一次使用才去实例化对象,后面使用的一直是这个对象。
代码:
public class Singleton {
private Singleton() {
}
private static Singleton single = null;//静态工厂方法
/**
* 懒汉式实例化对象,第一次使用才去实例化
*/
public static Singleton getInstance() {
if (single == null) {
single = new Singleton();
}
return single;
}
}
Singleton通过将构造方法限定为private避免了类在外部被实例化,在同一个虚拟机范围内,Singleton的唯一实例只能通过getInstance()方法访问。
事实上,通过Java反射机制是能够实例化构造方法为private的类的,那基本上会使所有的Java单例实现失效。此问题在此处不做讨论,姑且掩耳盗铃地认为反射机制不存在。
但是以上懒汉式单例的实现没有考虑线程安全问题,它是线程不安全的,并发环境下很可能出现多个Singleton实例。正确的做法是在getInstance方法上加同步。
//加同步锁synchronized,
public synchronized static Singleton getInstance() {
if (single == null) {
single = new Singleton();
}
return single;
}
上面使用的是同步方法,也是可以使用同步代码块,结果是一样的。同步的作用是防止多个类同时调用这个类时,会创建多个对象!
饿汉式单例模式:
饿汉嘛,说明这个类是很饿的,一来到就要吃东西,所有东西都要事先准备好的。
饿汉式在类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变,所以天生是线程安全的。
代码:
public class Singleton {
private Singleton() {
}
private static final Singleton single = new Singleton();
//静态工厂方法
public static Singleton getInstance() {
return single;
}
}
登记式单例模式:
登记单例模式是使用HashMap存储的,代码麻烦,累赘,不建议看和用。这里只是給大家简单展示一下。
代码:
public class Singleton3 {
private static Map<String, Singleton3> map = new HashMap<String, Singleton3>();
static {
Singleton3 single = new Singleton3();
map.put(single.getClass().getName(), single);
}
//保护的默认构造方法
protected Singleton3() {
}
//静态工厂方法,返还此类惟一的实例
public static Singleton3 getInstance(String name) {
if (name == null) {
name = Singleton3.class.getName();
System.out.println("name == null" + "--->name=" + name);
}
if (map.get(name) == null) {
try {
map.put(name, (Singleton3) Class.forName(name).newInstance());
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
return map.get(name);
}
//一个示意性的商业方法
public String about() {
return "Hello, I am RegSingleton.";
}
public static void main(String[] args) {
Singleton3 single3 = Singleton3.getInstance(null);
System.out.println(single3.about());
}
}
登记式单例实际上维护了一组单例类的实例,将这些实例存放在一个Map(登记薄)中,对于已经登记过的实例,则从Map直接返回,对于没有登记的,则先登记,然后返回。
这里对登记式单例标记了可忽略,首先它用的比较少,另外其实内部实现还是用的饿汉式单例,因为其中的static方法块,它的单例在类被装载的时候就被实例化了。
饿汉式和懒汉式区别
从名字上来说,饿汉就是类一旦加载,就把单例初始化完成,保证getInstance的时候,单例是已经存在的了,而懒汉比较懒,只有当调用getInstance的时候,才回去初始化这个单例。
另外从以下两点再区分以下这两种方式:
1.线程安全:
饿汉式天生就是线程安全的,可以直接用于多线程而不会出现问题,
懒汉式本身是非线程安全的,为了实现线程安全有几种写法,需要同步,加载和性能方面有些区别。
2.资源加载和性能:
饿汉式在类创建的同时就实例化一个静态对象出来,不管之后会不会使用这个单例,都会占据一定的内存,但是相应的,在第一次调用时速度也会更快,因为其资源已经初始化完成。
懒汉式顾名思义,会延迟加载,在第一次使用该单例的时候才会实例化对象出来,第一次调用时要做初始化,如果要做的工作比较多,性能上会有些延迟,之后就和饿汉式一样了。
什么是线程安全?
如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。
结论:
由结果可以得知单例模式为一个面向对象的应用程序提供了对象惟一的访问点,不管它实现何种功能,整个应用程序都会同享一个实例对象。
Android开发中也是经常用到单例模式
Android开发中的单例模式一般都是在继承Application类中实现的。
代码:
/**
* Application类
*/
public class App extends Application {
private static App sInstance;//全局对象
/**
* 全局获取单例
*/
public static App getInstance() {
return sInstance;
}
@Override
public void onCreate() {
super.onCreate();
//赋值
sInstance = this;
}
//定义一些全局变量和全局方法
}
Android中Application和Activity都是比较特殊的,都没有构造方法,都是不能new出来的,Application只要在AndroidManifest中注册,就会执行onCreate方法,这时就已经有这个全局对象了,并且不需要什么同步,不需要考虑什么懒汉、饿汉(其实是饿汉),因为Application是这个程序最先运行的类,其他任何调用类都是在之后运行的。
关于单例模式,记住最重要的一句话就可以:在一个程序中,这个类的对象是全局唯一。