Singleton模式主要作用是保证在Java应用程序中,一个类Class只有一个实例存在。在很多操作中,比如建立目录 数据库连接(当然多使用连接池)都需要这样的单线程操作。Singleton模式就为我们提供了这样实现的可能。使用Singleton的好处还在于可以节省内存,因为它限制了实例的个数,有利于Java垃圾回收(garbage collection)。
Singleton主要是在类自身定义一个静态的自身实例作为属性,并私有化,最后通过一个公有的方法获得这个实例。用static声明的变量在整个程序运行过程中,只会在编译时创建一次,所以保证了实例的单例化。
推荐一篇关于单例模式的文章:点击打开链接
饿汉形式是在编译时为声明一个对象,并且实例化,这样做无论程序是否调用这个实例,它都一直存在于内存中。但这样不会出现在运行过程中创建出多个实例的错误。
/** * 饿汉方式 */ public class Singleton { private static Singleton sgt = new Singleton(); private Singleton (){}//构造函数的隐藏 public static Singleton getSingleton(){ return sgt; } }
懒汉形式是在运行过程中,当程序需要这个类的一个实例时,才会开辟内存并实例化。但当程序有多个线程同时访问这段程序,有可能会创建多个实例,所以必需加上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; } }
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.1优点
1、单例模式只会产生一个对象,在内存中也有一个,节省内在空间2、避免频繁的创建和销毁对象,可以提高内部运行性能3、可以全局讯问
5.2注意
1、使用单例设计模式时,就应该考虑其反制机制和原型模式之间的冲突2、不要断开引用变量与单例类对象之间的联系3、使用高并发操作时,注意线程安全4、 单例是将其构造函数给隐藏了的,所以一般单例类是不能够被继承的,因为子类在实例化的过程会调用父类的构造函数,而单例没有构造函数提供给子类调用,所以 单例是不能够被继承的。但存在一种<< 不完全的单例类 >>,它的构造函数是公开的。这方面更深入的研究可参考《java与模式》一书
在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); //运行结果发现这个两个变量的引用地址不一样,说明产只产生了两个实例 }