引言:java
这一系列文章,翻译自网络上的文章,不过中间会夹杂着我的的理解,非原创,不过中文应该算是原创。
数据库
下面介绍,使用设计模式的一些好处:
设计模式
一、设计模式是已经在工业生产中使用的,用于解决特定问题的标准方法,若是你在遇到相似问题的时候,能参考相应的设计,会节省你不少的时间。
api
二、设计模式,每每能提升代码的重用性,会减小你的开发时间。
缓存
三、既然你们都知道设计模式,包括从你的类的命名上面,类的使用上面,你们都很清楚的知道相应类的具体功能,就像每一个人都约定好的那样。
安全
分类:
网络
java设计模式分为3大类:
多线程
一、对象建立
并发
二、结构
app
三、行为
对象建立设计模式的定义:
一、在特定的问题场景中,建立设计模式会合理的根据特定的问题建立对象。由于对象的建立可能会很复杂,会增长没必要要的复杂性,建立设计模式以不一样的建立方法解决了这些复杂性。
对象建立设计模式包含的范围:
一、单例
二、工厂
三、抽象工厂
四、建造者
五、原型
本节会讲一下,对象建立中的单例设计模式。
单例设计模式,大部分人第一印象感受很简单,看完这篇博客,我想你就不会这么认为啦,不一样的开发者对于以何种方式实现单例设计模式是有争议的,本文尽量的把目前实现单例的方法归纳出来,并给出最佳实战的建议。
单例模式的定义:
单例模式是一种建立对象实例的设计模式,在建立对象的过程当中,他会保证,在一个java虚拟机里面只有一个对象实例,同时单例设计模式,必须提供一个给外部访问该惟一对象的入口。
单例模式的应用场景:
日志、驱动对象、缓存、线程池等等,固然设计模式不是单一存在的,其它设计模式中也会使用单例设计模式来完成一些事情,好比:抽象工厂,建造者,原型,门面等。同时在java核心包里面也有其身影,好比java.lang.runtime.
单例模式的实现方法的共性:
一、提供私有的构造函数,防止外部类直接初始化该对象
二、提供私有的静态变量,固然要是该类的对象实例,惟一的对象实例。
三、public的静态方法,这就是上面说的,提供给外部类获取该类对象的惟一入口。
接下来,咱们会以不一样的方法,来实现单例设计模式。
【A】饥饿实现
饥饿实现就是,等不急,当类被classloader加载的时候就初始化了该对象,这种实现有个弊端就是,当client不使用该类实例就浪费啦,不过我我的以为,这种状况不存在,你不用他写他作什么呢?代码测试要有覆盖率的,测试每天在你后面催着喊你把他删掉。
public class EagerInitializedSingleton { private static final EagerInitializedSingleton instance = new EagerInitializedSingleton(); //private constructor to avoid client applications to use constructor private EagerInitializedSingleton(){} public static EagerInitializedSingleton getInstance() { return instance; } }
若是你的单例对象在建立的时候不使用过多的资源,这种方法是可行的。可是大部分的时候,单例对象建立的时候会加载不少的资源文件,好比File system,数据库链接等等。咱们应该避免在客户端调用getInstance方法以前建立这个对象,可是这种方法没法提供异常的捕获,异常的抛出,可能在建立对象的时候。
【B】静态块的实现方式
静态块的实现方式和上面的实现方式差很少,可是静态块的实现,能够处理异常。代码以下:
public class StaticBlockSingleton { private static StaticBlockSingleton instance; private StaticBlockSingleton(){} //static block initialization for exception handling static { try{ instance = new StaticBlockSingleton(); }catch(Exception e) { throw new RuntimeException("Exception occured in creating singleton instance"); } } public static StaticBlockSingleton getInstance() { return instance; } }
和饥饿的实现方式同样,当类被classloader加载的时候就初始化了该对象。
【C】延迟建立
顾名思义,就是须要的时候再去建立。
public class LazyInitializedSingleton { private static LazyInitializedSingleton instance; private LazyInitializedSingleton(){} public static LazyInitializedSingleton getInstance() { if(instance == null) { instance = new LazyInitializedSingleton(); } return instance; } }
上面这种实现方法,在单线程的环境下是工做良好的,可是,在多线程并发建立对象的时候会出现问题,简单的分析下缘由,考虑多线程并发安全的时候,首先要找到共享点,就是共享的对象,而后想象各类执行顺序,你会发现instance这个地方就是共享点,当多线程执行的时候,有可能会建立多个对象,因此该方法不能保证单例实现。
【D】线程安全的建立
为了解决多线程并发建立对象的问题,咱们引入Synchronized关键字,这样的话,同一时刻,只有一个对象能访问getInstance方法。
public class ThreadSafeSingleton { private static ThreadSafeSingleton instance; private ThreadSafeSingleton(){} public static synchronized ThreadSafeSingleton getInstance() { if(instance == null) { instance = new ThreadSafeSingleton(); } return instance; } }
这种方式是能防止多并发引发的多对象建立问题,可是写代码嘛,总要追求点东西,你说别人装逼也好,卖弄也罢,总之在某一方面比你好。在多并发中,HashMap和ConcurrentHashMap,你是锁住整个HashMap仍是HashMap的一个segment这是有质的区别的,因此改进的代码,只朝着一个方向,那就是下降锁的力度,因而乎,下面的代码使用double check来下降了力度,提升了性能。
public static ThreadSafeSingleton getInstanceUsingDoubleLocking() { if(instance == null) { synchronized (ThreadSafeSingleton.class) { if(instance == null) { instance = new ThreadSafeSingleton(); } } } return instance; }
还有一种实现叫Bill Pugh,java的版本更新变化很快,包括新出的java9,可能名字不是,你们也知道,金融在将来几年是个很火的领域,因而乎java9中集成了货币的api支持,这是个人猜想,哈哈,包括先在的招财宝,铜板街,挖财,听说王福强大神,跳到挖财啦,固然我也是作金融行业的业务,有兴趣的能够留言,听说要招人。这些题外话,可是不无用处哈,java的内存模型会致使上面的几种方法,在不少不少线程出现的时候,会出现问题,因而乎,这我的,也就是Bill Pugh本身实现了一下这种方法:
【E】Bill Pugh实现
public class BillPughSingleton { private BillPughSingleton(){} private static class SingletonHelper { private static final BillPughSingleton INSTANCE = new BillPughSingleton(); } public static BillPughSingleton getInstance() { return SingletonHelper.INSTANCE; } }
看代码发现Bill Pugh是经过私有静态内部类来实现的,当单例对象被classloader加载的时候,SingletonHelper是不会被加载到内存的,除非有对象调用getInstance方法,这是最经常使用的建立单例对象的方法,由于不须要锁,已经在多个项目中使用了这种方法,简单有效。
【F】Enum实现
public enum EnumSingleton { INSTANCE; public static void doSomething() { //do something } }
世界上好人和坏人老是都存在的,单例对象也不例外,总有那么几种方法是搞破坏的,费尽千辛万苦,建立的单例,有可能被破坏的。
【破坏者1】反射
【破坏者2】序列化
有破坏办法,就又解决办法,破坏者1的解决办法是ENUM,破坏者2的解决办法是重写readResolve方法。这两部分,你说是和反射有关呢,仍是和序列化有关呢,序列化都够我写一篇东西的了。下次再说
poke holes in the abstraction,and it starts leaking.