Java的设计模式一般可以划分为3部分。分别为creational(创建型)、结构型(structural)和行为型(behavioral)型
为什么这里强调Java,虽然设计模式更多是一种思想和套路,但也跟程序语言本身的特性也有一定的联系。例如Python,就很少用接口、多态这东西(虽然可以强行用abc包进行模拟)而更多使用函数作为参数进行。
在python进阶书籍《fluent python》也写到“Althought design pattern are language-independent , that does not mean every pattern applies to every language”。
这方面更多的讨论可以在参考知乎上的讨论:https://www.zhihu.com/question/63734103
这里不再进行展开。只能说,如果你的语言非java,没必要把每个设计模式都生拉硬扯到自己的代码上,也许自身的语言有更为简单、方便的实现方式。
当然,如果使用的是c++语言,根据万物都可c++这一定律,尽管放心使用即可。
creational(创建型)、结构型(structural)和行为型(behavioral)型的区别和联系
creational: 顾名思义,用于实例化对象的时候使用。虽然本人没做过统计,但毫无疑问,这是用得最多的设计模式!为啥?你都面向对象了还不得每次都创建一个对象出来?
structural:类和类之前的结构联系,更多关注的是类之间的交互方式。
behavioral: 不仅仅关注类和对象的结构,而且重点关注他们之间的相互作用,每个类之间的分工都有明确的不同职责。
creational还比较好进行理解,但structural和behavioral存在着一定的交叉部分,可以理解为两者只是强调点不同,structural更强调的是结构,behavioral强调的是行为和职责不同。个人感觉也没必要太在意,毕竟只是一个分类标准而已。
当然,进一步理解可以看这个bloghttps://blog.csdn.net/zhanduo0118/article/details/85602983
单例
终于到了这节的重点。单例,顾名思义为单个实例,即单个对象。在面向对象编程中常常出现这样一种情况:需要加载数据库的时候,你总希望只要加载一次——毕竟加载数据库是一件比较耗时的工作,而且你多次加载好像也不能带来什么增益。这在不是多线程的时候还稍微好办一点,反正你就用这个对象作为参数传来传去就行。但两个没有联系的类在调用同一个文件的时候呢?在多线程的情况下呢??这时候就需要用一种方法来保证该实例只加载一次!
当然,除了数据库的加载外,在Spring上Bean的加载也默认用上了单例这个模式(当然,可以改)。
即使有多种方式可以实现单例,然都需要遵循以下3个原则:
1. 私有化构造方法,杜绝了直接实例化的途径
2. 私有化类的自身实例,并将该实例设置为静态(static)
3. 提供一个静态方法用来返回2中的实例,也是该类的唯一对外的实例化方法。
实现1:Eager Initialization
public class EagerInitializedSingleton {
private static final EagerInitializedSingleton instance = new EagerInitializedSingleton();
private EagerInitializedSingleton(){
}
public static EagerInitializedSingleton getInstance(){return instance;}
}
这种方法也称为饿汉式,即只要代码中写入了这段程序,不管你要不要启动该类或者加载对应的数据库,通通都要实例化。
好处就是简单简单简单!如果你的程序一定要实例化该类的,请无脑选择该方式!
实例2:Static block initialization
public class StaticBlockSingleton {
private static StaticBlockSingleton instance;
private StaticBlockSingleton(){}
static {
try {
instance = new StaticBlockSingleton();
}catch (Exception e){
throw new RuntimeException("Exception occurred in creating singleton instance");
}
}
public static StaticBlockSingleton getInstance(){
return instance;
}
}
跟实例1几乎一摸一样,也是饿汉式,唯一的优势是多了一个异常处理。
实例3 懒汉式
public class LazyInitializedSingleton {
private static LazyInitializedSingleton instance;
private LazyInitializedSingleton(){}
private LazyInitializedSingleton getInstance(){
if(instance==null){
instance = new LazyInitializedSingleton();
}
return instance;
}
}
这种方式俗称“懒汉式”加载,只有在你需要它的时候,调用getInstance()方法才会调用。但是!!!该方法不能在多线程下使用,因为在多线程的情况并不能保证此时是单例的。
实例4 线程安全性的懒汉式
解决多线程最简单的方法就是——加锁
public class ThreadSafeSingleton {
private static ThreadSafeSingleton instance;
private ThreadSafeSingleton() {
}
public static synchronized ThreadSafeSingleton getInstance() {
if (instance == null) {
instance = new ThreadSafeSingleton();
}
return instance;
}
}
但这方法可以有效解决多线程中单例的线程安全性问题,但不够有效!因为该锁直接锁住的getInstance()方法,而实际上这里的线程安全性问题只会在new ThreadSateSingleton()中出现。因此,更常用的是以下这种方式:
class ThreadSafeSingleton2{
private static ThreadSafeSingleton2 instance;
private ThreadSafeSingleton2() {
}
// using double check
public static ThreadSafeSingleton2 getInstance2(){
if(instance==null){
synchronized (ThreadSafeSingleton2.class){
if(instance==null){
instance = new ThreadSafeSingleton2();
}
}
}
return instance;
}
}
这种也称为double check方法。为什么要使用double check?先说只有一个条件判断——假设我们线程1,2,3进入到synchronized那里(synchronized肯定要在第一个判断语句后,不然压根没有性能的提升)。线程1获得了锁,线程2,3都阻塞在这里。线程1实例化了对象,然后释放了锁。假如接下来线程2获得了锁,因为第二步没有再进一步条件判断,将直接调用instance = new ThreadSafeSingleton2()方法,此时单例失效。但如果有第二个if判断,则不会再次实例化。
这种double check方法也是目前本人最喜欢和常用的单例方法,也建议大家使用这个。
实例5 BillPugh单例
据说(只是据说)在java 1.5之前jdk的锁存在一定的问题(这个确实不知道,java1.5的时候我还在读小学.......)。因此1.5版本的java有时候double check方法也会有一定的问题。因此书上也介绍了另一种方法,用静态内部类来进行懒汉式方法的实现。
public class BillPughSingleton {
private BillPughSingleton(){
}
private static class SingletonHelper{
private static final BillPughSingleton INSTANCE = new BillPughSingleton();
}
public static BillPughSingleton getInstance(){
return SingletonHelper.INSTANCE;
}
}
当然,现在都2020年了,几乎也没有什么公司还会用java1.5的进行开发(最起码也要java8了吧),但感觉这种方法还是有必要学学的——毕竟你接手的不一定是今年才开始写的代码。事实上这代码也是作者最推崇的单例构造模式。
如果你说我司还在用1.5版本的?我觉得最好的方法是——换一家公司吧
博客介绍了Java设计模式,分为创建型、结构型和行为型,强调设计模式与语言特性有关。重点阐述单例模式,指出在数据库加载、Spring的Bean加载等场景常用,还介绍了饿汉式、懒汉式等多种单例实现方式及优缺点。

276

被折叠的 条评论
为什么被折叠?



