1. 设计模式——单例

博客介绍了Java设计模式,分为创建型、结构型和行为型,强调设计模式与语言特性有关。重点阐述单例模式,指出在数据库加载、Spring的Bean加载等场景常用,还介绍了饿汉式、懒汉式等多种单例实现方式及优缺点。

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版本的?我觉得最好的方法是——换一家公司吧

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值