设计模式之单例模式——Singleton
设计意图:
保证类仅有一个实例,并且可以供应用程序全局使用。为了保证这一点,就需要这个类自己创建自己的对象,并且对外有公开的调用方法。而且,别的类不能实例化它,所以构造方法要设置为私有的。
单例模式的要点
一是某个类只能有一个实例;
二是它必须自行创建这个实例;
三是它必须自行向整个系统提供这个实例。
例如:
有一个"单例对象",而"客户甲"、"客户乙" 和"客户丙"是单例对象的三个客户对象。可以看到,所有的客户对象共享一个单例对象。而且从单例对象到自身的连接线可以看出,单例对象持有对自己的引用。一些资源管理器常常设计成单例模式。在计算机系统中,需要管理的资源包括软件外部资源,譬如每台计算机可以有若干个打印机,但只能有一个Printer Spooler, 以避免两个打印作业同时输出到打印机中。每台计算机可以有若干传真卡,但是只应该有一个软件负责管理传真卡,以避免出现两份传真作业同时传到传真卡中的情况。每台计算机可以有若干通信端口,系统应当集中管理这些通信端口,以避免一个通信端口同时被两个请求同时调用。
实现的方法:
单例模式有很多种,下面就来介绍一下单例模式的几种使用方法吧:
【饿汉模式】饿汉法就是在第一次引用该类的时候就创建对象实例,而不管实际是否需要创建。
public class Singleton{ private static final Singleton singleton = new Singleton(); /** * 私有构造函数 */ private Singleton(){ } /** * 获得实例 * @return */ public static Singleton getInstance(){ return singleton; } public static void say(){ System.out.println("单例模式创建啦!"); } }
public class main { public static void main(String[] args) { main m=new main(); Singleton.getInstance().say(); } }
这样做的好处是编写简单,但是无法做到延迟创建对象。但是我们很多时候都希望对象可以尽可能地延迟加载,从而减小负载,所以就需要下面的懒汉法:
【懒汉法】
这种方法可以实现延时加载,但是有一个致命弱点:线程不安全。如果有两条线程同时调用getSingleton()方法,就有很大可能导致重复创建对象。
public class Singleton { private static Singleton singleton = null; private Singleton(){} public static Singleton getSingleton() { if(singleton == null) singleton = new Singleton(); return singleton; } }
解决线程不安全的三种方法:
1、在getInstance方法上加同步
public static synchronized Singleton getInstance() { if (single == null) { single = new Singleton(); } return single; }
2、双重检查锁定
synchronized (Singleton.class),相当于在Singleton对象上加了锁,任何一个线程如果的到了这把锁就可以先运行,直到到他执行完其他线程才有可能得到这把锁,再进入执行。
public static Singleton getInstance() { if (singleton == null) { synchronized (Singleton.class) { if (singleton == null) { singleton = new Singleton(); } } } return singleton; }
3、静态内部类
public class Singleton { private static class LazyHolder { private static final Singleton INSTANCE = new Singleton(); } private Singleton (){} public static final Singleton getInstance() { return LazyHolder.INSTANCE; } }
思考:java中单例模式和静态类(一个类,所有方法为静态方法)的区别
由于单例模式和静态类都具有良好的访问性,它们之间有许多相似之处,例如,两者可以直接使用而无须创建对象,都可提交唯一实例,在一个非常高的高度上看起来它们都为是用于同样的任务。
1.那么在java中什么时候该使用单例模式,什么时候该使用静态类呢?
如果你的单例不需要维护任何状态,仅仅提供全局访问的方法,这种情况考虑使用静态类,静态方法比单例更快,因为静态的绑定是在编译期就进行的。但是要记住,不建议在静态类中维护状态信息,特别是在并发环境中,若无适当的同步措施而修改多线程并发时,会导致坏的竞态条件。
如果你需要将一些工具方法集中在一起时,你可以选择使用静态方法,但是别的东西,要求单例访问资源时,应该使用单例模式。
2.Java中单例(变现类)与静态类(表现方法)的区别
1)静态类比单例具有更好的性能,因为静态方法在编译期绑定。
2)再次,它们的区别是override的能力,因Java中的静态方法是不可以覆盖的,这就导致其木有太多的灵活性,另一面,你可通过继承的方式覆盖单例类中定义的方法。
3)静态类很难模拟,因此难于单例测试,单例更容易模拟,因为也比静态类易于编写单元测试,不论神马单例期望神马,你都可以传递模拟对象,例如构造方法或方法参数。
4)如果你的需求中需要维护状态信息,则单例比静态类更适合,因为后者在维护状态信息方面是非常可怕的,并导致狡滑的bug。
5)如果是一个非常重的对象,单例可以懒加载,但是静态类没有这样的优势,并且非常热切的加载。
6)许多依赖注入的框架对单例都有良好的管理,例如Spring,使用它们非常容易。
3.Java中,选择单例而不是静态类的优点
单例与静态主要的优点是前者比后者更具有面向对象的能力,使用单例,可以通过继承和多态扩展基类,实现接口和更有能力提供不同的实现,如果我们讨论java.lang.Runtime,在Java中它是单例,调用getRuntime()方法,会基于不同的JVM返回不同的实现,但也保证了每个JVM中实有一个实例,如果java.lang.Runtime是一个静态类,不太可能因不同的JVM返回不同的实现。
这就是Java中单例与静态类的区别,当你需要一个全能力的对象时,选择单例,如果仅仅是将一些静态方法预售,使用静态类。