设计模式-创建模式-单列模式

单列模式

一、概念

1.单列模式的概念:

​ 单例模式是一种常见的设计模式,它确保一个类只有一个实例存在,并提供一个全局访问点来获取该实例。

主要目的是控制某些类的实例化过程,以保证在整个应用程序中只有一个该类的实例被创建和使用。

2.组成

​ 单列模式 = 饿汉式 + 懒汉式

二、饿汉式

1.概念:

​ 类加载就会导致该单实例对象被创建

2.优点:

​ (1)线程安全,由于在类加载时就创建了实例,不存在并发创建实例的问题,因此天然是线程安全的。在多线程环境下,无需额外的同步机制就能保证只有一个实例被创建。

​ (2)在单例对象被加载时就初始化,不会造成加载速度慢的问题。

​ (3)没有加锁机制,执行效率高

3.缺点:

​ 类加载创建对象 不管用不用该对象,都已经开辟好的内存就会闲置,造成系统资源的浪费。

4.代码实现:

(1)方式一:
package com.wt.singlemode;

/**
 * 单列模式 -- 饿汉式
 * 实现方式 静态变量
 * 饿汉式:类加载就会导致该单实例对象被创建
 */
public class Animal {
    //私有构造方法 外界无法创建该类对象   ----  反射除外 还有其它方法应对反射破坏单列的解决办法 TODO
    private Animal(){}

    //静态变量创建对象
    private static Animal animal= new Animal();

    //对外提供方法获取对象
    public static Animal getAnimal(){
        return animal;
    }
}
(2)方式二:
package com.wt.singlemode;

/**
 * 单列模式 饿汉式
 * 实现方式 静态代码块
 */
public class AnimalByStatic {
    private AnimalByStatic() {
    }

    private static AnimalByStatic animalByStatic;

    /**
     * 初始化静态变量:‌静态代码块可以用于初始化类中的静态变量。‌在类加载时,‌静态代码块会对静态变量进行赋值操作,‌确保它们在程序运行前被正确地初始化。‌
     * 执行一次性初始化操作:‌静态代码块在类加载时只执行一次,‌因此非常适合执行一些只需要进行一次的初始化操作,‌如读取配置文件、‌建立数据库连接等。‌
     * 预加载资源:‌通过静态代码块,‌可以在类加载时预加载一些资源,‌如常用的数据,‌以提高程序的性能并减少后续操作的延迟。‌
     */
    static {
        animalByStatic = new AnimalByStatic();
    }

    public static AnimalByStatic getAnimalByStatic(){
        return animalByStatic;
    }

}

三、懒汉式

1.概念:

​ 类加载不会导致该单实例对象被创建,而是首次使用该对象时才会创建

2.优点:

​ 延迟初始化:第一次使用对象时才创建这个对象,从而节省了内存。

​ 因为没有在类被加载时就进行实例化,所以它可以避免在系统启动时就占用系统资源。

3.缺点:

​ 线程不安全:原始的懒汉式实现通常不是线程安全的。在多线程环境下,如果多个线程同时访问 getInstance 方法,并且此时实例尚未创建,可能会导致创建多个实例,从而破坏单例模式的唯一性。

​ 性能开销:为了解决线程安全问题,通常需要添加同步机制(如 synchronized 关键字),这可能会带来一定的性能开销,尤其是在高并发场景下。

4.代码实现:

(1)方式一:
package com.wt.lazystyle;

/**
 * 单列模式 -- 懒汉式
 * 懒汉式:类加载不会导致该单实例对象被创建,而是首次使用该对象时才会创建
 * 缺点 线程不安全
 */
public class Car {
    private Car() {
        System.out.println(Thread.currentThread().getName() + "ok");
    }

    private static volatile Car car;

    //解决线程不安全 加一个同步锁 每次读都需要持锁访问 其余的线程需要等待 性能很低 解决 双重检查方法
//    public static synchronized  Car getCar() {
//        if (car == null) {
//            car = new Car();
//        }
//        return car;
//    }
    //双重检查方法 但多线程条件下 Car对象有可能出现null异常 解决 在car变量添加volatile关键字
    public static Car getCar() {
        if (car == null) {
            synchronized(Car.class) {
                if (car == null) {
                    car = new Car();
                }
            }

        }
        return car;
    }
}
(2)方式二(推荐静态内部类实现单列模式的懒汉模式):
package com.wt.lazystyle;

/**
 * 静态内部类实现单列模式的懒汉模式
 */
public class CarByStaticInnerClass {
    private CarByStaticInnerClass(){}
    /**
     * 静态内部类 jvm在加载外部类的过程中,是不会加载静态内部类的
     * 只有内部类的属性或方法被调用时才会被加载,并初始化其静态属性
     * static修饰只能被实例化一次,并严格保证实例化顺序
     */
    private static class InnerByCar{
        private static final CarByStaticInnerClass CAR = new CarByStaticInnerClass();
    }

    public static CarByStaticInnerClass getCar(){
        return InnerByCar.CAR;
    }
}

四、通过反射机制和反序列化可以破坏单例模式

1.代码演示通过反射机制破坏单列模式:

package com.wt.lazystyle;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

/**
 * 通过反射机制破坏单列模式
 */
public class BreakSingleModeToCarByInner {
    public static void main(String[] args) throws Exception{
        //1.获取class对象
        Class carByStaticInnerClassClass = CarByStaticInnerClass.class;
        //2.获取该类构造方法
        Constructor declaredConstructor = carByStaticInnerClassClass.getDeclaredConstructor();
        //3.取消访问权限
        declaredConstructor.setAccessible(true);
        //4.获取对象
        Object object1 = declaredConstructor.newInstance();
        Object object2 = declaredConstructor.newInstance();
        //判断是否为同一个对象 false:不是(也就代表通过反射破坏了单列模式) ture:是
        System.out.println(object1 == object2);//false
    }
}

2.代码演示反射机制破坏单列模式的解决方法:

package com.wt.lazystyle;

/**
 * 静态内部类实现单列模式的懒汉模式
 */
public class CarByStaticInnerClass {
    /**
     * 因为静态内部类没有提供该类的变量(CAR常量对象),所以需要创建一个bool类型进行逻辑判断
     * false 表示第一次访问,没有创建对象 true 非第一次访问
     */
    private static boolean flag = false;

    private CarByStaticInnerClass(){

        //多线程条件下 得需要同步代码块
        /**
         * 反射破解单列模式需要添加的代码
         */
        synchronized(CarByStaticInnerClass.class){
            if (flag){
                throw new RuntimeException("不能创建多个对象");
            }
            flag = true;
        }
    }
    /**
     * 静态内部类 jvm在加载外部类的过程中,是不会加载静态内部类的
     * 只有内部类的属性或方法被调用时才会被加载
     * 并初始化其静态属性
     * static修饰只能被实例化一次,并严格保证实例化顺序
     */
    private static class InnerByCar{
        private static final CarByStaticInnerClass CAR = new CarByStaticInnerClass();
    }

    public static CarByStaticInnerClass getCar(){
        return InnerByCar.CAR;
    }
}

3.运行结果

(1)没处理之前

在这里插入图片描述

(2)处理之后

在这里插入图片描述

  • 9
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值