1. 什么是单例模式及其特点?
单例模式:即保证一个java类在java虚拟机中仅仅有一个实例对象。
单例模式主要有三个特点:
A) 单例类确保自己只有一个实例
B) 单例类必须自己创建自己的实例
C) 单例类必须为其他对象提供唯一实例
2. 单例模式的好处,为什么要用单例模式?
保证正常使用,在java虚拟机中只有一个对象,减少开销
3.话不多说,我们来看具体代码实现
a) 饿汉式
//饿汉式
public class Singleton {
//饿汉式,随着类的加载初始化对象
private static Singleton instance=new Singleton();
//将构造方法隐藏
private Singleton() {
// TODOAuto-generated constructor stub
}
//饿汉模式获取实例对象
public static Singleton getInstance(){
return instance;
}
}
注解:由于instance是Singleton的静态变量,随着类的加载而加载并初始化,还没调用getInstance方法时就已经存在java虚拟机中,即称为饿汉式。因为随着类的加载而过早创建对象,占用虚拟机内存,如果实例化instance很好资源,则会大大降低了内存使用率。因此,需要改进方式,在调用getInstance方法获取对象实例时才对instance初始化,于是就有懒汉式。
b) 懒汉式(线程不安全)
public class Singleton {
// 懒汉式,随着类的加载没有初始化对象
private static Singleton instance = null;
// 将构造方法隐藏
private Singleton() {
// TODO Auto-generated constructor stub
}
// 懒汉式调用方法时初始化对象
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
注解:只有在调用getInstance方法时,才对instance初始化。如果instance为null,则new一个Singleton对象,如果不为null,即instance已初始化,返回instance。当线程A和线程B同时进入到if(instance==null)中时,即instance=new Singleton();会执行两次,创建不止一个对象。因此这种方式效率高,但线程不安全,只适合单线程使用。
c) 懒汉式(线程安全)
//懒汉式(线程安全)
public class Singleton {
// 懒汉式,随着类的加载没有初始化对象
private static Singleton instance = null;
// 将构造方法隐藏
private Singleton() {
// TODO Auto-generated constructor stub
}
// 懒汉式调用方法时初始化对象
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
注解:为了防止b)中情况的发生,在getInstance方法上加同步锁,即同一时刻只能有一个线程进来,避免了创建出多个对象的可能。但每次调用时都要获取同步锁,加锁很耗时,只有当instance为null且需要初始化时,才需要加同步锁,所以没有必要每次获取对象都要获取同步锁,所以有了双重验证式。
d) 双重验证式
//双重验证
public class Singleton {
private static Singleton instance = null;
private Singleton() {
// TODO Auto-generated constructor stub
}
public static Singleton getInstance() {
if (instance == null) {
synchronized(Singleton.class){
if(instance==null){
instance = new Singleton();
}
}
}
return instance;
}
}
注解:第一重验证,当instance为null时,才需要加同步锁来创建对象,当instance不为空时,则不需要加同步锁,提高了效率。但代码实现较为复杂。
e) 静态内部类式
//静态内部类
public class Singleton {
private Singleton() {
// TODO Auto-generated constructor stub
}
//静态内部类
private static class SingletonHolder{
private static final Singleton INSTANCE = new Singleton();
}
//获取静态内部类中Singleton 对象 INSTANCE
public static Singleton getInstance(){
return SingletonHolder.INSTANCE;
}
}
注解:这种方式利用了ClassLoader工作机制,保证了instance初始化时只有一个线程。它不同于饿汉式,饿汉式当Singleton类加载时,instance初始化;而现在Singleton类加载时,SingletonHolder并没有主动加载,即没有初始化instance,在调用getInstance方法时加载SingletonHolder而初始化instance。
f) 枚举式
public enum SingletonEnum {
INSTANCE;
public void show() {
System.out.println("我是枚举式"+INSTANCE);
}
}
枚举式测试
SingletonEnum instance1=SingletonEnum.INSTANCE;
instance1.show();
SingletonEnum instance2=SingletonEnum.INSTANCE;
instance2.show();
//判断是否是同一对象
if(instance1==instance2){
System.out.println("同一个instance");
}else {
System.out.println("不是同一个instance");
}
测试结果表明是同一个实例对象。
最后还有一种枚举式,它不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象
综合以上,我们可以将b)和c)归为一类—懒汉式,即单例模式可以归纳为5中方式:饿汉,懒汉,双重验证,静态内部类,枚举。此处我们先无视java反射机制,暂时当它不存在。往后会继续更新java单例与反射的内容。
参考:http://www.cnblogs.com/hupp/p/4487521.html
参考:http://www.blogjava.net/kenzhh/archive/2013/03/15/357824.html