呕心整理Java中的12种常用设计模式以及应用场景

目录

单例模式

饿汉模式

懒汉模式

线程锁模式

双重判断模式

静态内部类模式

策略模式

工厂模式

简单工厂

抽象工厂

门面模式

装饰器模式

责任链模式

享元模式

观察者模式

代理模式

静态代理模式

动态代理模式

适配器模式

原型模式

模板方法模式


单例模式

        单例模式是java中老生常谈的设计模式,在工作中相信大家也没少接触,就小编个人而言,单例模式的主要应用场景如下:

        适用于项目中频繁获取对象的场景,例如:获取缓存对象、获取一些工具类对象等等,由于这些对象使用频率较高,所以在获取对象时,我们使用单例模式指定获取一个对象即可。

下面小编带大家再次温习一下单例模式的写法,这里将介绍单例模式的五种写法,

饿汉模式

 代码结构如下:

  1. 私有的静态的 最终的 对象 直接new
  2. 私有的 无参构造方法
  3. 共有的 静态的 实例方法

        饿汉模式是单例模式中常用的写法之一,主要的特点是在定义对象的时候就直接new一个对象,详细代码如下:

/**
 * @{NAME}
 * @Description TODO
 * @Author luocong
 * @Date
 * @Version 1.0
 * 单例模式 饿汉式
 *  上来就new对象
 **/
public class SignletonHungry {

    //1. 私有的静态的最终的对象
    private static final SignletonHungry singl=new SignletonHungry();

    //2. 私有的无参构造函数
    private SignletonHungry(){

    }

    //3. 公共的静态的实例方法
    public static SignletonHungry getInstance(){
        return singl;
    }

    //测试方法
    public static void main(String[] args) {
        //利用for循环 模拟多线程环境调用
        for (int i = 0; i < 100; i++) {
            new Thread(()->{
                //看每次获取对象的hashcode是否一致 判断是否获取了同一个对象
                System.out.println("获取的hashCode是: "+SignletonHungry.getInstance().hashCode());
            }).start();
        }
    }
}

        在类的下方我们定义了一个测试方法,用来模拟验证在多线程并发访问时,每次通过单例类获取的对象的hashcode方法是否一致,如果一致则代表线程安全,执行结果如下:

可以看到执行结果是一致的,这种饿汉写法一般在实际项目中应用的也是最多的,

优点:这种写法比较简单,就是在类装载的时候就完成实例化,避免了线程同步问题。

缺点:但是因为在指定对象时就进行初始化,在类比较大的时候,也会造成一定的资源消耗。

懒汉模式

代码结构如下:

  1. 私有的静态的对象 为空 不new
  2. 私有的无参构造方法
  3. 共有的静态的实例方法

        为了避免上述所说的饿汉式的缺点,延伸出了懒汉式的单例写法,在定义对象时,并不直接进行初始化,在实际的实例化方法里面,才进行初始化操作,这样就节省了一定的资源,具体实现代码如下:

/**
 * @{NAME}
 * @Description TODO
 * @Author luocong
 * @Date
 * @Version 1.0
 *
 * 单例模式 懒汉式
 * 调用实例方法时才new对象
 * 节省空间 缺点是线程不安全
 **/
public class SignletonFull {

    //1. 私有的静态的对象 先不new 默认为null值
    private static SignletonFull signletonFull;

    //2. 私有的无参构造器
    private SignletonFull(){}

    //3. 公共的静态的方法
    public static SignletonFull getInstance() throws InterruptedException {
        if(signletonFull==null){
            Thread.sleep(1000);
            signletonFull=new SignletonFull();
        }
        return signletonFull;
    }

    //测试方法
    public static void main(String[] args) {
        //利用for循环 模拟多线程环境调用
        for (int i = 0; i < 100; i++) {
            new Thread(()->{
                //看每次获取对象的hashcode是否一致 判断是否获取了同一个对象
                try {
                    System.out.println("获取的hashCode是: "+SignletonFull.getInstance().hashCode());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

        同样,在这个类的下方我们也定义了一个测试方法,用来模拟验证在多线程并发访问时,每次通过单例类获取的对象的hashcode方法是否一致,如果一致则代表线程安全,执行结果如下:

        我们可以看到,懒汉模式在多线程并发获取单例类时,存在现场安全的问题,那么既然存在线程安全问题,我们怎么去改善这个问题呢?请看线程锁模式。

线程锁模式

 代码结构如下:

  1. 私有的静态的对象 为空 不new
  2. 私有的无参构造方法
  3. 共有的静态的实例方法,在判断对象是否为空时加上synchronize修饰

        通过线程锁的写法可以解决懒汉模式下存在的线程安全问题,具体实现代码如下:

/**
 * @{NAME}
 * @Description TODO
 * @Author luocong
 * @Date
 * @Version 1.0
 *
 * 单例模式之 加锁
 * 线程安全 缺点是效率低 受synchronized锁升级的影响
 **/
public class SignletonThread {
    //1. 私有的静态的对象
    private static SignletonThread signletonThread;

    //2. 私有的构造方法
    private SignletonThread(){}

    //3. 公共的静态的实例方法 在if里面加上锁synchronized
    public static SignletonThread getInstance(){
        if (signletonThread==null){
            synchronized (SignletonThread.class){
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                signletonThread=new SignletonThread();
            }
        }
        return signletonThread;
    }

    //测试方法
    public static void main(String[] args) {
        //利用for循环 模拟多线程环境调用
        for (int i = 0; i < 100; i++) {
            new Thread(()->{
                //看每次获取对象的hashcode是否一致 判断是否获取了同一个对象
                System.out.println("获取的hashCode是: "+SignletonThread.getInstance().hashCode());
            }).start();
        }
    }
}

        同样,在这个类的下方我们也定义了一个测试方法,用来模拟验证在多线程并发访问时,每次通过单例类获取的对象的hashcode方法是否一致,如果一致则代表线程安全,执行结果如下: 

         我们可以看到,执行结果并不如人意,为什么呢,这是因为在执行到synchronized代码快的时候,有线程已经获取到了对象,从而导致获取的对象不一致的情况,那么如何解决这个问呢?

双重判断模式

代码结构如下:

  1. 私有的静态的对象 为空 不new
  2. 私有的无参构造方法
  3. 共有的静态的实例方法,在判断对象是否为空时加上synchronize修饰
  4. 在判断里面再次判断是否为空
package designmodel.signelton;

/**
 * @{NAME}
 * @Description TODO
 * @Author luocong
 * @Date
 * @Version 1.0
 *
 * 单例写法
 * 双重判断式
 **/
public class SignletonThreadTwo {

    //1. 私有的静态的对象
    private static SignletonThreadTwo signletonThreadTwo;

    //2. 私有的构造方法
    private SignletonThreadTwo(){}

    //3. 公共的静态的实例方法 在if里面加上锁synchronized 在锁块中继续判断是否为空
    public static SignletonThreadTwo getInstance(){
        if (signletonThreadTwo==null){
            synchronized (SignletonThreadTwo.class){
                if(signletonThreadTwo==null){
                    signletonThreadTwo=new SignletonThreadTwo();
                }
            }
        }
        return signletonThreadTwo;
    }

    //测试方法
    public static void main(String[] args) {
        //利用for循环 模拟多线程环境调用
        for (int i = 0; i < 100; i++) {
            new Thread(()->{
                //看每次获取对象的hashcode是否一致 判断是否获取了同一个对象
                System.out.println("获取的hashCode是: "+SignletonThreadTwo.getInstance().hashCode());
            }).start();
        }
    }
}

        同样,在这个类的下方我们也定义了一个测试方法,用来模拟验证在多线程并发访问时,每次通过单例类获取的对象的hashcode方法是否一致,如果一致则代表线程安全,执行结果如下:  

        我们看执行结果,虽然执行结果是我们想要的,但是由于引入了synchronized代码块,所以也引入了轻量级锁、重量级锁的概念,虽然保障了线程安全,但是却失去了性能加成并且容易导致死锁,所以,有没有什么办法,既能线程安全,又能保障效率呢? 

静态内部类模式

代码结构如下:

  1. 私有的无参构造器
  2. 私有的静态的内部类
  3. 在内部类中定义私有的最终的静态的对象,new一个
  4. 定义公共的实例方法,返回内部类.对象
package designmodel.signelton;

/**
 * @{NAME}
 * @Description TODO
 * @Author luocong
 * @Date
 * @Version 1.0
 * 单例模式
 * 通过静态内部类实现懒加载与线程安全
 * 利用JVN特性实现 JVM在加载类和内部类的时候 只会在运行的时候加载一次 从而保证线程安全和懒加载
 **/
public class SignletonStaticClass {

    //1. 私有的无参构造器
    private SignletonStaticClass(){}

    //2. 私有的静态的内部类
    private static class SignletonStatic{
        //3. 在私有的内部类中定义私有的 最终的 静态的对象
        private final static SignletonStaticClass signletonStaticClass=new SignletonStaticClass();
    }

    //4. 公共的静态的实例方法
    public static SignletonStaticClass getInstance(){
        return SignletonStatic.signletonStaticClass;
    }

    //测试方法
    public static void main(String[] args) {
        //利用for循环 模拟多线程环境调用
        for (int i = 0; i < 100; i++) {
            new Thread(()->{
                //看每次获取对象的hashcode是否一致 判断是否获取了同一个对象
                System.out.println("获取的hashCode是: "+SignletonStaticClass.getInstance().hashCode());
            }).start();
        }
    }

}

        在执行验证代码时,我们可以看到,通过此写法保障了线程安全和效率,这个写法的原理类似于饿汉式,利用JVN特性实现 JVM在加载类和内部类的时候 只会在运行的时候加载一次 从而保证线程安全和懒加载。

策略模式

代码结构如下:

  1. 定义策略接口,定义通用方法。
  2. 定义N个实现类,实现接口,重新方法。
  3. 定义Context上下文类,利用多态进行封装。
  4. 使用时通过Context上下文类进行调用,在构造函数中传入实现类的对象。

     策略模式:策略模式是一种行为型模式,它将对象和行为分开,将行为定义为 一个行为接口 和 具体行为的实现。策略模式最大的特点是行为的变化,行为之间可以相互替换。每个if判断都可以理解为就是一个策略,可以使得算法可独立于使用它的用户而变化。

    使用场景:

                       1. 假设现在某超市有三个等级的会员,普通会员,VIP1,VIP2。

                       2. 在结账的时候,三个登记的会员购买了同一种商品,普通会员不打折,VIP1打9折,VIP2打8折

定义策略接口:

package designmodel.celve;

/**
 * @Author luocong
 * @Description //TODO
 * @Date 12:20 2022/11/8
 * @Param 
 * @return
 * 定义策略接口
 * 案例场景:
 *  有三种会员 购买相同数量和单价的产品时 需要打不同的折扣
 **/
public interface StrategyInt {

    //price价格 n数量
    public double getPrice(double price,int n);
}

定义普通会员,实现策略接口:

package designmodel.celve;

/**
 * @{NAME}
 * @Description TODO
 * @Author luocong
 * @Date
 * @Version 1.0
 * 实现类1 实现接口中定义的计算价格方法
 * 普通会员类 不打折
 **/
public class NormalPerson implements StrategyInt {

    //普通会员不打折
    @Override
    public double getPrice(double price, int n)
    {
        System.out.println("普通会员不打折.....");
        return (price*n);
    }
}

 定义vip1会员,实现策略接口:

package designmodel.celve;

/**
 * @{NAME}
 * @Description TODO
 * @Author luocong
 * @Date
 * @Version 1.0
 *实现类2 实现接口中定义的计算价格方法
 *VIP1会员 打9折
 **/
public class Vip1Person implements StrategyInt{

    //VIP1客户 打9折
    @Override
    public double getPrice(double price, int n) {
        System.out.println("VIP1打9折.....");
        return (price*n)*0.9;
    }
}

 定义vip2会员,实现策略接口: 

package designmodel.celve;

/**
 * @{NAME}
 * @Description TODO
 * @Author luocong
 * @Date
 * @Version 1.0
 * 实现类2 实现接口中定义的计算价格方法
 * VIP2会员类 打8折
 **/
public class Vip2Person implements StrategyInt {

    @Override
    public double getPrice(double price, int n) {
        System.out.println("VIP2打8折...
MySQL是一种关系型数据库管理系统,被广泛应用于各类网站、应用程序企业级系统中。它是由瑞典MySQL AB公司开发的,后来被Sun Microsystems收购,最终成为了Oracle公司的一部分。 MySQL以其高性能、稳定性可靠性而闻名,不仅能够处理大规模的数据处理需求,还具备较高的扩展性可定制性。它支持多用户访问并发操作,并提供了完善的安全性权限管理机制,可灵活控制用户对数据的访问权限。 MySQL采用了客户端-服务器架构,其中客户端可以是各种编程语言实现的应用程序,而服务器则负责存储处理数据。MySQL使用了一种基于SQL(Structured Query Language,结构化查询语言)的查询语言,通过执行SQL语句实现数据的存储、检索管理。 MySQL支持多种存储引擎,如InnoDB、MyISAM、MEMORY等,每个存储引擎都有其特定的优势适用场景。同时,MySQL还支持事务处理数据备份恢复等常用功能,保证了数据的完整性可靠性。 MySQL拥有庞大的用户群体强大的社区支持,用户可以通过官方文档、在线论坛社交媒体等渠道获取帮助交流经验。此外,MySQL还有丰富的第三方工具插件生态系统,可提供更多功能扩展性。 总之,MySQL作为一种成熟、可靠的关系型数据库管理系统,被广泛应用于各类场景。它的强大功能、高性能可扩展性使其成为了开发者们首选的数据库解决方案之一。
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

只为code醉

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值