Java常用设计模式————单例模式

单例模式简介

90%以上的设计模式都或多或少的应用了接口和抽象类,而单例比较特殊,并没有接口的应用。

单例Singleton指仅仅被实例化一次的类。通常被用来代表那些本质上唯一的系统组件。————《Effective Java》

数据库连接获取类的对象可以是单例的。

单例的意思就是在内存中只有一个对象。而单例和static有是有区别的,static是用来修饰类中的成员变量和成员方法的,而单例则属于对象的层面。

单例的实现

思考:如何在内存中只有一个对象?——不能让外界随意的实例化(new)。封装思想中有一个叫“属性私有化,方法公开化”的概念,在需要单例的类中自己去实例化。这就是单例的两个条件:不能被外界实例化;根据封装的特征属性私有化,方法公开化。

外界不能实例化,实现的方式就是将需要设置为单例的类(以下简称:单例类)的构造器设置为private。

注意:如果电脑上安装了多个虚拟机的话,那么实际上并不是真正意义上的单例,而是每个虚拟机会有一个单例。

懒汉单例模式:

懒汉单例模式在第一次使用对象的时候才会去创建对象,但最简单的懒汉单例存在多线程安全问题,依然有可能创建多个实例。

/*** 懒汉单例模式Demo<br>懒汉单例是在运行时调用的一种行为。* 单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。<br>* 这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。<br>* 这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。<br>* 这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。<br>* * 类名:LazySingleton<br>* 作者: mht<br>* 日期: 2018年3月18日-下午8:41:01<br>*/
public class LazySingleton {/** 属性私有化 */private static LazySingleton instance = null;/** 构造器私有化 */private LazySingleton() { }/** 公开获取单例对象 */public static LazySingleton getInstance() {if (instance == null) {instance = new LazySingleton();}return instance;}
}

饿汉单例模式:

饿汉单例会在类加载的时候即实例化对象,这与懒汉单例的创建时机截然不同,不存在线程安全问题,

但缺点是需要提前占用内存资源。

/*** 类名:EagerSingleton<br>* 作者: mht<br>* 日期: 2018年3月18日-下午8:41:01<br>*/
public class EagerSingleton {/** 使用静态常量*/private static final EagerSingleton instance = new EagerSingleton();private EagerSingleton() {System.out.println("这是构造器:LazySingleton()");}public static EagerSingleton getInstance() {return instance;}public void doSomething() {System.out.println("EagerSingleton is doing something now ...");}
}

懒汉+饿汉(静态内部类):

该方法通过私有静态内部类来实现单实例延迟加载,因为静态内部类不会因为外部类的加载而加载,所以可以在第一次使用静态类的时候才执行加载。
优点是:不仅可以达到懒汉式的延迟加载,使类的加载速度提升,避免一开始占用过多内存,又具备线程安全性,无需判断,性能上也会更快一些。

/*** 作者: mht<br>* 日期: 2018年3月19日-下午9:55:52<br>*/
public class Singleton {private Singleton () {System.out.println("Singleton...");}private static class SingletonInstance {private static final Singleton s = new Singleton();}public static Singleton getInstance() {return SingletonInstance.s;}
}

双重检查-单例模式(线程安全的懒汉式单例):

双重检查单例模式 使用双重检查同步延迟加载来创建单例的做法是一个非常优秀的做法, 其不但保证了单例(线程安全),而且切实提高了程序的运行效率。

实现原理:

Java语言提供了一种较synchronized稍弱的同步机制,volatile,用来确保将变量的更新操作通知到其他线程。当把变量声明为volatile类型后,编译器运行时都会注意到这个变量是共享的,因此不会将该变量上的操作与其他内存操作一起重排序。volatile变量不会被缓存在寄存器或者对其他处理器不可见的地方,因此在读取volatile类型的变量时总会返回最新写入的值。

getInstance()方法描述:

为了在保证单例的前提下提高运行效率,我们需要对单例对象dcs进行第二次检查,目的是避开过多的同步(因为这里的同步只需在第一次创建实例时才同步,一旦创建成功,以后获取实例时就不需要同步获取锁了)。这种做法无疑是优秀的,但是我们必须注意一点:必须使用volatile关键字修饰单例对象

public class DoubleCheckSingleton {// 使用volatile关键字防止重排序,因为 new Instance()是一个非原子操作。private static volatile DoubleCheckSingleton dcs;private DoubleCheckSingleton() {}public static DoubleCheckSingleton getInstance() {if (dcs == null) {synchronized (DoubleCheckSingleton.class) {// 只需在第一次创建实例时才同步if (dcs == null) {dcs = new DoubleCheckSingleton();}}}return dcs;}
}

注册单例模式:

public class RegisterSingleton<T> {/* 注册表 */private static Map<String, Object> regMap = new HashMap<>();// 类加载过程中,静态初始化static{Connection conn = new Connection();UserService us = new UserService();// 初始化注册表regMapregMap.put(conn.getClass().getName(), conn);regMap.put(us.getClass().getName(), us);}private RegisterSingleton() { }public synchronized static Object getInstance(String key) {if (key == null) return null;try {if (regMap.get(key) == null) {// 这里注意,此Demo是以类名作为key传入map中的,因此,这里的传值也是相关方法,通过类名找到类,然后在进行自动实例化regMap.put(key, Class.forName(key).newInstance());}return regMap.get(key);} catch (ClassNotFoundException e) {e.printStackTrace();} catch (InstantiationException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();}return null;}
}

测试:

package design.pattern;public class testSingleton {public static void main(String[] args) {UserService us = (UserService) RegisterSingleton.getInstance(UserService.class.getName());Connection conn1 = (Connection) RegisterSingleton.getInstance(Connection.class.getName());Connection conn2 = (Connection) RegisterSingleton.getInstance(Connection.class.getName());System.out.println(us);System.out.println(conn1);System.out.println(conn2);System.out.println(conn1 == conn2);}
}

运行结果:

design.pattern.UserService@15db9742
design.pattern.Connection@6d06d69c
design.pattern.Connection@6d06d69c
true

对单例模式应用的理解:

在spring框架中大多数对象的使用都是单例模式的,比如获取用户User对象的方法类UserDao和UserService都是单例的,包括controller,他们默认都单例的。因为实际上我们真正需要多个对象的是User而并不是获取User的方法类。

这也就充分解释了Spring注解中的@Autowired自动注入对象的实现方式。细心的我们也都可以发现,这些自动注入的对象一般都是实现某种业务的中间类对象,而并不是最终我们需要的对象,因此,为了避免频繁的创建这些“中间件”对象占用不必要的内存空间,都是以单例的形式来创建的。另外,数据库连接对象Connection也一定是单例的。

单例模式的误解:

单例会导致线程阻塞吗?不会的。

举个生活中的例子:饭店只有一个菜谱,但是有多个厨师,多个厨师共享这份菜谱,并同时做一道菜,彼此之间是没有影响的;老师在黑板上写下一个数学题,同学们在下面计算结果,黑板就是内存,数学题就是单例对象,同学们就是各个线程,计算过程之间是没有影响的。

单例模式的优点:

1.内存中只有一个对象,节省内存空间

2.避免频繁的创建和销毁对象,可以提高性能

3.避免对共享资源的多重占用,简化访问

4.为整个系统提供一个全局访问点

单例模式的应用场景:

在计算机系统中,线程池、缓存、日志对象、对话框、打印机、显卡驱动程序对象常被设计为单例的。实际上,这些应用都或多或少具有资源管理器的功能。

其核心在于为整个系统提供一个唯一的实例,其应用场景包括但不仅限于一下几种:

1.有状态的工具类

2.频繁访问数据库或文件的对象

参考:

《彻头彻尾理解单例模式以及其在多线程环境中的应用》

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/559034.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

最简明扼要的 Systemd 教程,只需十分钟

systemctl常用命令介绍~~~Systemctl是一个systemd工具&#xff0c;主要负责控制systemd系统和服务管理器。 Systemd是一个系统管理守护进程、工具和库的集合&#xff0c;用于取代System V初始进程。Systemd的功能是用于集中管理和配置类UNIX系统。启动及服务大多数主流发行版要…

Java常用设计模式————工厂模式

简介&#xff1a; 工厂模式&#xff08;Factory Pattern&#xff09;是Java中最常用的设计模式之一&#xff0c;又称多态工厂模式、虚拟构造器模式。属于创建型模式。 在工厂模式中&#xff0c;我们在创建对象时不会对客户端暴露创建逻辑&#xff0c;并且是通过使用一个共同的…

JavaScript新手入门教程大全~~~

JavaScript新手入门教程大全~~~一。 js教程介绍&#xff1a;JavaScript是一种运行在浏览器中的解释型的编程语言。 那么问题来了&#xff0c;为什么我们要学JavaScript&#xff1f;因为你没有选择。在Web世界里&#xff0c;只有JavaScript能跨平台、跨浏览器驱动网页&#xff0…

Java常用设计模式————抽象工厂模式

简介 每一个具体工厂类只负责创建抽象产品的某一个具体子类的实例。 与工厂方法模式的区别 工厂方法模式针对的是一个产品等级结构&#xff0c;而抽象工厂模式针对的是多个产品等级结构&#xff0c;因此抽象工厂模式在结构上要比工厂方法模式更加复杂和抽象&#xff0c;也更…

CSS新手入门教程~~~~

CSS新手入门教程~~~~CSS简介&#xff1a;什么是 CSS? CSS 指层叠样式表 (Cascading Style Sheets)样式定义如何显示 HTML 元素样式通常存储在样式表中把样式添加到 HTML 4.0 中&#xff0c;是为了解决内容与表现分离的问题外部样式表可以极大提高工作效率外部样式表通常存储在…

Docker必备知识整理

Docker简介 Docker是一个开源的应用容器引擎&#xff0c;让开发者可以打包他们的应用以及依赖包到一个可移植的容器中&#xff0c;然后发布到任何流行的Linux机器上&#xff0c;也可以实现虚拟化。容器是完全使用沙箱机制&#xff0c;相互之间不会有任何接口。 Docker是用Go语言…

Exception和Error深入分析~~~

Exception和Error深入分析~~~Exception 和 Error 都是继承了 Throwable 类&#xff0c;在 Java 中只有 Throwable 类型的实例才可以被抛出&#xff08;throw&#xff09;或者捕获&#xff08;catch&#xff09;&#xff0c;它是异常处理机制的基本组成类型。 Exception 和 Erro…

Java常用设计模式————原型模式(一)

介绍 原型模式&#xff08;Prototype Pattern&#xff09;&#xff1a;用原型实例指定创建对象的种类&#xff0c;并通过拷贝这些原型创建新的对象。 原型模式用于创建重复的对象&#xff0c;同时又能保证性能。当直接创建对象的代价比较大时&#xff0c;则采用这种模式。&…

解决eclipse刚启动卡死的问题~~~亲测有效~~

解决eclipse刚启动卡死的问题~~~亲测有效~~ 重启了eclipse三次&#xff0c;还是卡死了。最后找到一下方法&#xff1a; 建议大家在删除先备份一下文件&#xff0c;以免丢失项目&#xff01; 关于 eclipse启动卡死的问题&#xff08;eclipse上一次没有正确关闭&#xff0c;导致…

Java常用设计模式————原型模式(二)之深拷贝与浅拷贝

引言 clone顾名思义就是复制&#xff0c; 在Java语言中&#xff0c; clone方法被对象调用&#xff0c;所以会复制对象。所谓的复制对象&#xff0c;首先要分配一个和源对象同样大小的空间&#xff0c;在这个空间中创建一个新的对象。那么在java语言中&#xff0c;有几种方式可…

Java控制台输入教程~~~~

Java控制台输入教程~~~~当通过new Scanner&#xff08;System.in&#xff09;创建一个Scanner&#xff0c;控制台会一直等待输入&#xff0c;直到敲回车键结束&#xff0c;把所有输入的内容传给Scanner&#xff0c;作为扫描对象。如果要获取输入的内容&#xff0c;则只需要调用…

Java面试宝典————基础篇

参考原文&#xff1a;《Java面试题全集&#xff08;上&#xff09;》 1.Java中的基本数据类型有哪些&#xff1f; 类型&#xff1a;byte short int long float double boolean char 字节&#xff1a;1 2 4 8 4 8 1 2 2.面向…

Git初学札记(零)————EGIT完成Eclipse到GitHub一条龙

eclipse安装Egit插件 首先我们要找到所需的egit插件的url更新地址。百度一大堆&#xff0c;但是我还是希望自己去寻找。 打开Eclipse Downloads官网&#xff0c;在页面底部直接输入“egit”关键字&#xff0c;并直接点击第一条搜索到的结果。然后点击Downloads标签页&#xff0…

如何使用echo指令向文件写入内容

如何使用echo指令向文件写入内容0.前言本文总结如何使用echo命令向文件中写入内容&#xff0c;例如使用echo指令覆盖文件内容&#xff0c;使用echo指令向文件追加内容&#xff0c;使用echo指令往文件中追加制表符。echo向文件中输出内容的基本方法是使用IO重定向指令——“>…

Eclipse生成SSH传输密钥并实现GitHub的SSH代码提交

生成公私密钥 打开eclipse首选项完成如下操作&#xff1a;保存密钥&#xff1a;这里注意&#xff0c;博主之前已经生成过密钥了&#xff0c;因此这里只是演示截图&#xff0c;如果此时点击保存&#xff0c;会弹出“是否覆盖”提示框。 其中&#xff0c;id_rsa代表非对称加密算法…

Java实现链表结构

Java实现链表结构按链表的组织形式分有ArrayList和LinkList两种。ArrayList内部其实是用数组的形式实现链表&#xff0c;比较适合链表大小确定或较少对链表进行增删操作的情况&#xff0c;同时对每个链表节点的访问时间都是constant&#xff1b;而LinkList内部以一个List实现链…

SpringBoot————快速搭建springboot项目

完成项目的创建信息 浏览器打开SPRING INITIALIZR网址&#xff1a; http://start.spring.io/ 如下图所示完成配置&#xff1a; 1.完成基础项目配置 2.相关名称 3.依赖jar包&#xff0c;如果是web项目&#xff0c;那么这里选择的Web依赖已经包含了开发web项目所必须的服务器…

史上最容易理解————GET和POST两种基本请求方法的区别

GET和POST两种基本请求方法的区别GET和POST是HTTP请求的两种基本方法&#xff0c;要说它们的区别&#xff0c;接触过WEB开发的人都能说出一二。最直观的区别就是GET把参数包含在URL中&#xff0c;POST通过request body传递参数。你可能自己写过无数个GET和POST请求&#xff0c;…

SpringBoot————JPA快速使用

本篇博客源码地址&#xff1a;https://github.com/DragonWatcher/ease-run 概述 Hibernate与JPA 本篇博客中的web项目选用Hibernate作为持久层框架。在Spring Boot中&#xff0c;我们需要了解另一个概念&#xff1a;JPA 上一句话可能有些歧义&#xff0c;并不是说JPA就是Sp…

MySQL优化建议汇总~~~

MySQL优化建议汇总~~~1、将经常要用到的字段&#xff08;比如经常要用这些字段来排序&#xff0c;或者用来做搜索&#xff09;&#xff0c;则最好将这些字段设为索引 2、字段的种类尽可能用int或者tiny int类型。另外字段尽可能用not null 3、当然无可避免某些字段会用到text&a…