Java十分钟快速掌握单例模式

目录
  • 前言
  • 1、什么是单例模式:
  • 2、单例模式的优缺点:
  • 3、懒汉模式(比较常用)
  • 4、饿汉模式【推荐使用】
  • 5、单例模式的应用场景
  • 6、单例模式的应用实例
  • 小结:

前言

首先在Java中有23种设计模式:

创建型模式: 工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式结构型模式: 适配器模式、装饰者模式、代理模式、外观模式、桥接模式、组合模式、享元模式行为型模式::策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

1、什么是单例模式:

定义:

指一个类只有一个实例,且该类能自行创建这个实例的一种模式。可以避免因打开多个任务管理器窗口而造成内存资源的浪费,或出现各个窗口显示内容的不一致等错误。比如咱们电脑是不是只能打开一个任务管理器?对吧,这就是为了防止资源浪费和其他错误。

项目中一般可以通过单例模式来获取同一个对象来调用工具方法,这样的好处是节约内存资源,我没有必要创建多个不同的对象,因为这样消耗内存资源

简而言之: 单例就是程序只有一个实例,该类负责创建自己的对象,同时要确保只有一个对象创建

单例模式的特点:

  • 构造器私有
  • 持有自己类型的属性
  • 对外提供获取实例的静态方法

单例模式的结构图:

2、单例模式的优缺点:

优点:

  • 减少了内存的开销
  • 避免对资源的多重占用
  • 设置全局访问点,可以优化和共享资源的访问

缺点(参考自互联网):

  • 一般没有接口,扩展困难。如果要扩展,则除了修改原来的代码,没有第二种途径,违背开闭原则
  • 在并发测试中,单例模式不利于代码调试。在调试过程中,如果单例中的代码没有执行完,也不能模拟生成一个新的对象
  • 单例模式的功能代码通常写在一个类中,如果功能设计不合理,则很容易违背单一职责原则

看一张单例模式的思维导图:

3、懒汉模式(比较常用)

懒汉模式特征是延迟初始化,在调用方法获取实例的时候才会实例化对象 线程不安全,严格意义上来说不是单例模式,优势是在获取实例才会创建对象因此更节省内存开销

Demo:

public class SingLeton {

    //1、有自己类型的属性
    private static SingLeton instance;

    //2、构造器私有化
    private SingLeton(){}

    //3、对外提供获取实例的静态方法
    public static SingLeton getInstance(){
        if (instance == null){
            instance = new SingLeton();
        }
        return instance;
    }

}

测试类:

public class Test {
    public static void main(String[] args) {

        //判断是否产生的是同一个对象
        SingLeton s1 = SingLeton.getInstance();
        SingLeton s2 = SingLeton.getInstance();
        System.out.println(s1 == s2);
    }
}

输出:

true

注意

关于懒汉模式线程非安全

现在知道懒汉模式的线程是非安全的,那么就需要使用锁(synchronized )来同步:

/**
 *   保证 instance 在所有线程中同步
 */
public class SingLeton2 {

        //1、有自己类型的属性
        private static volatile SingLeton2 instance ;    

        //2、构造器私有化
        private SingLeton2() {
        }

        public static synchronized SingLeton2 getInstance() {
            //getInstance 方法前加同步
            if (instance == null) {
                instance = new SingLeton2();
            }
            return instance;
        }
    }

如果是写多线程,则不要删除上例代码中的关键字 volatile 和 synchronized,否则将存在线程非安全的问题。如果不删除这两个关键字就能保证线程安全,但是每次访问时都要同步,会影响性能,且消耗更多的资源,这是懒汉式单例的缺点。

4、饿汉模式【推荐使用】

饿汉模式线程安全,常用,但是容易产生垃圾对象,因为饿汉模式一开始加载类的时候就初始化 了实例

Demo:

/**
 *
 * 饿汉模式
 */
public class SingLeton {

    //持有自己类型的属性   (和懒汉一样)
    //由于static修饰,只在类加载的时候执行一次,类加载的时候就实例化对象
    private static SingLeton instance = new SingLeton();

    //构造器私有化,不能通过它创建对象
    private SingLeton(){};

    //对外提供获取实例的静态方法
    public static SingLeton getInstance(){
        return instance;
    }
}

测试类:

public class Test {
    public static void main(String[] args) {

        //判断是否产生的是同一个对象
        SingLeton s1 = SingLeton.getInstance();
        SingLeton s2 = SingLeton.getInstance();
        System.out.println(s1 == s2);
    }
}

输出:

true

懒汉模式和饿汉模式对比:

  • 懒汉模式延迟加载,非线程安全,饿汉模式线程安全
  • 懒汉模式刚运行不实例化对象,需要的时候才实例化对象,相当于来讲更节省内存开销
  • 饿汉模式只要运行都会加载类的时候就给你初始化了,就需要使用更大的内存

图解:

5、单例模式的应用场景

  • 需要经常创建的一些类,使用单例可以降低系统的内存压力
  • 这个类只要求生成一个对象的时候,比如每个人的名字
  • 类创建实例时占用资源较多,或实例化耗时较长,且经常使用
  • 频繁访问数据库或文件的对象
  • 类需要频繁实例化,而创建的对象又频繁被销毁的时候,如多线程的线程池

6、单例模式的应用实例

这里使用懒汉式单例模式模拟产生班级的班长 分析: 在每一个学期内,班级的班长只有一人,所以适合用单例模式实现

Person类:

/**
 * 使用懒汉模式
 */

public class Person {

    //保证instance在所有线程中同步
    private static volatile Person instance;

    private Person(){
        System.out.println("产生一个班长");
    }

    //加上synchronized锁
    public static synchronized Person getInstance(){
        if(instance == null){
            instance = new Person();
        }else {
            System.out.println("错误信息:已经有一个班长,不能再产生");
        }
        return instance;
    }

    public void getName(){
        System.out.println("我是班长:小强");
    }
}

测试类:

public class Test {
    public static void main(String[] args) {

        Person p1 = Person.getInstance();
        p1.getName(); //输出班长名字

        Person p2 = Person.getInstance();
        p2.getName();

        if(p1 == p2){
            System.out.println("两个班长是同一个人");
        }else {
            System.out.println("两个班长是同一个人");

        }
    }
}

运行结果:

产生一个班长
我是班长:小强
错误信息:已经有一个班长,不能再产生
我是班长:小强
两个班长是同一个人

小结:

这个就是单例模式,当程序已经产生一个对象后,就不会产生一个新的对象,即使有多个对象也是同一个对象而已,在使用懒汉模式的时候需要注意线程安全问题,在平时更加推荐使用饿汉模式,也需要注意资源的占用。

到此这篇关于Java十分钟快速掌握单例模式的文章就介绍到这了,更多相关Java 单例模式内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Java中常用的设计模式之单例模式详解

    目录 注意 优点 缺点 使用场景 一.实现方式 二.实现方式 三.测试 总结 注意 1.单例类只能有一个实例. 2.单例类必须自己创建自己的唯一实例. 3.单例类必须给所有其他对象提供这一实例. 优点 1.在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例(比如管理学院首页页面缓存). 2.避免对资源的多重占用(比如写文件操作). 缺点 1.没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化. 使用场景 1.要求生产唯一序列号. 2.WE

  • Java设计模式之单例模式深入探索

    目录 什么是设计模式? 单例模式是什么? 单例模式设计的原则是什么? Java实现单例模式的5种方式? 懒汉 饿汉 静态内部类 双重校验锁DCL(Double Check Lock) 枚举(num) 小结 ❤️‍您好,我是贾斯汀,今天来聊一聊单例模式!❤️‍ 什么是设计模式? 百科: 设计模式是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结. 设计模式是软件行业的通用的设计标准,在Java同样通用,主要有23种设计模式如下: 有的小伙伴可能会问,这么多,学得完吗? 答:不好意思

  • Java设计模式系列之深入浅出单例模式

    目录 前言 饿汉式 懒汉式 线程安全问题 volatile的作用 总结 前言 我不知道大家工作或者面试时候遇到过单例模式没,面试的话我记得我当时在17年第一次实习的时候,就遇到了单例模式,面试官是我后来的leader,当时就让我手写单例,我记得我就写出了饿汉式,懒汉式,但是并没说出懒汉和饿汉的区别,当时他给我一通解释我才知道了其中的奥秘. 写这篇文章之前我刻意的在我手上的项目里面去找了找,我发现单例在每个项目里面都有运用到,而且我后面所说的几种实现还基本上都涉及了,还挺有意思的. 开篇我就给大家

  • Java设计模式之单例模式示例详解

    目录 0.概述 1.饿汉式 1.1 饿汉式单例实现 1.2 破坏单例的几种情况 1.3 预防单例的破坏 2.枚举饿汉式 2.1 枚举单例实现 2.2 破坏单例 3.懒汉式 4.双检锁懒汉式 5.内部类懒汉式 6.JDK中单例的体现 0.概述 为什么要使用单例模式? 在我们的系统中,有一些对象其实我们只需要一个,比如说:线程池.缓存.对话框.注册表.日志对象.充当打印机.显卡等设备驱动程序的对象.事实上,这一类对象只能有一个实例,如果制造出多个实例就可能会导致一些问题的产生,比如:程序的行为异常.

  • 深入理解Java设计模式之单例模式

    目录 一.什么是单例模式 二.单例模式的应用场景 三.单例模式的优缺点 四.单例模式的实现 1.饿汉式 2.懒汉式 3.双重加锁机制 4.静态初始化 五.总结 一.什么是单例模式 单例模式是一种常用的软件设计模式,其定义是单例对象的类只能允许一个实例存在. 许多时候整个系统只需要拥有一个的全局对象,这样有利于我们协调系统整体的行为.比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息.这种方式简

  • java设计模式-单例模式实现方法详解

    目录 饿汉式 静态变量 静态代码块 懒汉式 线程不安全 线程安全 双重检查 静态内部类 总结 a 饿汉式 所谓饿汉式,就是直接创建出类的实例化,然后用private私有化,对外只用静态方法暴露. 静态变量 步骤 构造器私有化 类的内部创建对象 向外暴露一个静态的公共方法 优点 缺点 写法简单,在类装载的时完成实例化,避免了线程同步问题 类装载时完成实例化,没有达到LazyLoading的效果,若该实例从未使用,则会造成内存浪费 class Singleton { //私有化构造器 private

  • Java十分钟快速掌握单例模式

    目录 前言 1.什么是单例模式: 2.单例模式的优缺点: 3.懒汉模式(比较常用) 4.饿汉模式[推荐使用] 5.单例模式的应用场景 6.单例模式的应用实例 小结: 前言 首先在Java中有23种设计模式: 创建型模式: 工厂方法模式.抽象工厂模式.单例模式.建造者模式.原型模式结构型模式: 适配器模式.装饰者模式.代理模式.外观模式.桥接模式.组合模式.享元模式行为型模式::策略模式.模板方法模式.观察者模式.迭代子模式.责任链模式.命令模式.备忘录模式.状态模式.访问者模式.中介者模式.解释

  • Java十分钟快速掌握Stream流

    1.什么是Stream流: Stream 是Java 8 提出的一个新概念,不是输入输出的 Stream 流 (和IO流其实没有任何关系哈),而是一种使用函数式编程方式在集合类上进行操作的工具.简而言之,是以内部迭代的方式处理集合数据的操作,内部迭代可以将更多的控制权交给集合类.Stream 和 Iterator 的功能类似,只是Iterator 是以外部迭代的形式处理集合数据的操作. 当然Stream也有自己特性: 不是一种数据结构,不会存数据,只是在原数据集上定义了一组操作 这些操作是惰性的

  • Java十分钟入门多线程下篇

    目录 1.线程池: 2.创建线程池: 1.newCacheThreadPool: 2.newSingleThreadExecutor: 3.newFixedThreadPool(inta): 4.newScheduledTreadPool: 3.线程池创建自定义线程: 4.Runnable和Callable的区别: 5.线程池总结: 1.线程池: 什么是线程池? 咱们也不看长篇大论,通俗的来讲,线程池就是装线程的容器,当需要用的时候去池里面取出来,不用的时候放回去或者销毁.这样一个线程就可以反复

  • Java十分钟入门多线程中篇

    目录 1.线程的调度: 1.设置优先级(Priority): 2.休眠(sleep) 3.强制运行(join) 4.礼让(yield) 2.定时器线程: 3.线程的同步: 举例说明: 我们知道飞机在天上飞行是有固定的航线(可以理解成线程),每个机场都有最大的运行负载能力,当运行情况超过了负载能力的时候,这就需要塔台调度参与,会根据每架飞机的优先级排序.当在航线的时候,如果出现紧急情况,会让其他飞机避让,让这架飞机优先级提高,先降落.这就是调度,计算机程序线程运行也是这样的. 1.线程的调度: 在

  • Java十分钟入门多线程上篇

    什么是多线程? 在学习前,我们先对程序.进程.线程.并行.并发有个基础的概念了解: 程序: 为完成指定任务,用编程语言编写的一组指令的集合,即指一段静态的代码,静态对象. 进程: 是程序的一次执行过程,是一个动态的过程,进程自身有产生.使用和消亡的过程.(也称为生命周期,在后面会介绍) 线程: 进程可进一步细化为线程,是一个程序内部的一条执行路径,也就是进程内有线程 并行: 指两个或者多个事件在同一时刻发生,(同时发生) 并发: 指两个或者多个事件在同一个时段内发生,(并不是同时发生) 更好的理

  • Java十分钟精通反射机制原理

    什么是反射? 反射机制是在运行状态中,它为Java提供一种"操作对象"的能力,在运行状态下,通过Class文件对象,可以调用到任何类里面的属性.方法.以及构造方法,包括私有的,所有的类在反射机制面前都是透明的 自己的概括:通过Class文件对象可以看到这个类里面的所有东西,并且可以使用和修改 反射的前提是获取Class文件对象((字节码对象),那么一共有三种方式获取: Class.forName("全类名") ----通过Class类的静态方法(最常用) 类名.cl

  • Java十分钟精通集合的使用与原理上篇

    目录 什么是集合? 集合分为Collection和Map两种体系 一.Collection接口: 二.Map接口下分为HashMap和TreeMap: 集合总结: Collections工具类: 什么是集合? 比如我们去买超市买很多东西,我们不可能拿一样就去收银台,我们可能是先放到购物车内,然后再统一处理,所以购物车相当于一个容器,可以装很多东西,在Java中的集合也是相当于一个容器,可以装很多数据. 集合继承关系图: 但是这张图太复杂了,我们看一张简便的: 集合分为Collection和Map

  • Java十分钟精通集合的使用与原理下篇

    List集合: ArrayList: 底层是数组结构,储存有序并且可以重复的对象 package SetTest; import java.util.ArrayList; import java.util.Collections; import java.util.List; public class ArrayListTest { public static void main(String[] args) { //创建ArrayList的对象 List<Integer> list = ne

  • Java十分钟精通Log4j日志的使用

    目录 为什么要用日志? 下载: 详细步骤: 一.打开IDEA 二.创建日志对象 为什么要用日志? 我们知道,程序在运行过程中会产生很多很多信息,比如在几点几分的时候运行了,运行的结果是怎么样的?为了我们更加了解程序运行的情况,可以通过日志来查看,日志可以在控制台输出,也可以输出至指定的文件内,在下面的文章中具体的给大家介绍. 下载: Log4J是Apache公司的开源项目,用于日志处理.下载地址: https://logging.apache.org/log4j/2.x/download.htm

  • Java十分钟精通异常处理机制

    目录 异常处理机制的底层原理 异常的继承关系图 异常的处理 一.try-catch-finally结构 二.多catch处理不同的异常: 三.throws声明异常/throw抛出异常: 四.自定义异常: 五.常见的异常 异常处理机制的底层原理 抛出异常,在执行一个方法时,如果发送了异常,则这个方法生成代表该异常的一个对象,停止当前执行的 路径,并把异常提交给jre. 捕获异常:jre得到该异常后,虚招相应的代码来处理该异常.jre在方法的调用栈中查找,从生成异常的 方法开始回溯,直到找到相应的异

随机推荐