创建模式——单例模式

1、什么是单例模式

Singleton模式主要作用是保证在Java应用程序中,一个类Class只有一个实例存在。在很多操作中,比如建立目录 数据库连接(当然多使用连接池)都需要这样的单线程操作。Singleton模式就为我们提供了这样实现的可能。使用Singleton的好处还在于可以节省内存,因为它限制了实例的个数,有利于Java垃圾回收(garbage collection)。

Singleton主要是在类自身定义一个静态的自身实例作为属性,并私有化,最后通过一个公有的方法获得这个实例。用static声明的变量在整个程序运行过程中,只会在编译时创建一次,所以保证了实例的单例化。

推荐一篇关于单例模式的文章:点击打开链接

创建模式——单例模式_第1张图片

2、饿汉形式

饿汉形式是在编译时为声明一个对象,并且实例化,这样做无论程序是否调用这个实例,它都一直存在于内存中。但这样不会出现在运行过程中创建出多个实例的错误。
/**
 * 饿汉方式 
 */
public class Singleton {
	private static Singleton sgt = new Singleton();
        private Singleton (){}//构造函数的隐藏
	public static Singleton getSingleton(){
		return sgt;
	}
}

3、懒汉方式

懒汉形式是在运行过程中,当程序需要这个类的一个实例时,才会开辟内存并实例化。但当程序有多个线程同时访问这段程序,有可能会创建多个实例,所以必需加上synchronized关键字,防止上述错误的发生。

/**
 * 懒汉方式
 */
public class Singleton {
	private static Singleton sgt = null;
        private Singleton (){}<span style="font-family: Arial, Helvetica, sans-serif;">//构造函数的隐藏</span>
	public synchronized static Singleton getSingleton() {//第一把同步锁
		if (sgt == null) {
			synchronized (Singleton.class) {//第二把同步锁是为了防止java重排序时,导致产生多个实例			
				sgt = new Singleton();
			}
		}
		return sgt;
	}
}

4、使用java反射机制可以创建多个实例

public static void main(String[] args) throws Exception  {
		Class clazz=Singleton.class;//获得Singleton的字节码
		Constructor<Singleton> cons=clazz.getDeclaredConstructor();//获得默认的构造函数
		cons.setAccessible(true);//将这个函数的访问权限打开,不再是private
		Singleton sgt1 = cons.newInstance();
		Singleton sgt2 = cons.newInstance();
		System.out.println(sgt1);
		System.out.println(sgt2);
		//运行结果发现这个两个变量的引用地址不一样,说明产生了两个实例
		sgt1=Singleton.getSingleton();
		sgt2=Singleton.getSingleton();
		System.out.println(sgt1);
		System.out.println(sgt2);
		//运行结果发现这个两个变量的引用地址是一样,说明产只产生了一个实例
	}

当使用反射机制去创建一个实例时,是直接去内存加载这个类的字节码,而通常所指的单例是由jvm自动去加载字节码,再通过static private等修饰手法使某个类在运行时只产生一个实例,而反射相当于绕过了【平常使用static private等修饰手法限制实例的产生】这一过程,自己拿着字节码就去产生一个新的实例了。这样就使得单例不再是单例了。

5、单例模式的优点与注意事项

5.1优点

1、单例模式只会产生一个对象,在内存中也有一个,节省内在空间
2、避免频繁的创建和销毁对象,可以提高内部运行性能
3、可以全局讯问

5.2注意

1、使用单例设计模式时,就应该考虑其反制机制和原型模式之间的冲突
2、不要断开引用变量与单例类对象之间的联系
3、使用高并发操作时,注意线程安全
4、 单例是将其构造函数给隐藏了的,所以一般单例类是不能够被继承的,因为子类在实例化的过程会调用父类的构造函数,而单例没有构造函数提供给子类调用,所以 单例是不能够被继承的。但存在一种<< 不完全的单例类 >>,它的构造函数是公开的。这方面更深入的研究可参考《java与模式》一书

6、与原型模式的冲突

在java中,基本上要实现了Cloneable这一接口,重写clone()这一方法,就完成了一个原型类,但如果一个单例类也这样去做,就破坏了原有单例机制,可以用克隆的手法直接忽略了构造函数的限制。因为对象的克隆是通过Object类的clone这一个本地方法去完成的,它可以直接操作内存中的数据,去复制一份一模一样的对象,根本不会去调用构造函数,原型模式甚至可以忽略类中所有访问限制(private,protect),所以单例模式与原型模式是一对相矛盾的设计模式。
public static void main(String[] args) throws Exception  {
		Singleton sgt1,sgt2;
		sgt1=Singleton.getSingleton();
		sgt2=(Singleton) sgt1.clone();
		System.out.println(sgt1);
		System.out.println(sgt2);
		//运行结果发现这个两个变量的引用地址不一样,说明产只产生了两个实例
	}


你可能感兴趣的:(设计模式)