最近空闲的时候回顾了一下常用的设计模式,其中单例模式是创建型模式中比较基础的一种设计模式,说起单例模式,想必大家并不模式,我们都知道的是,在单例模式下,能够保证一个类只有一个实例对象,就是说,外部访问这个对象的时候,只有一个全局的访问点,究竟里面的原理是什么呢?或许代码很容易看明白,但搞懂里面的原理和思想在开发中可以很方便的转移到其他的开发中,下面结合具体的代码进行说明,

项目结构如下,比较简单,

这里讲讲述单例模式的三种形式,普通标准形式的,懒汉模式和饿汉模式下的单例模式;

我们知道,在大型项目中,经常会涉及到众多连接数据库的操作,连接数据库的文件我们通常的做法是放在配置文件里,但即使是这样,在高并发的环境下,如果没有其他的处理,访问数据库的连接次数仍然会非常的庞大和密集,这对于IO和数据库来说是非常大的开销,为了尽量减少连接的开销和浪费,这里如果我们采用单例模式的思想,在第一次读取了配置文件之后,连接数据库的信息就已经存在了,后面其他的连接只有经过单例,就不会再频繁的执行连接数据库的操作;

图中,使用application.properties模拟数据库的连接信息,ReadPropertiesUtils为读取配置文件的工具类,可以拿来直接使用,

application.properties内容如下:

driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/manage_pool1
username=root
password=root

ReadPropertiesUtils工具类代码,也是比较常用的读取配置文件的方式,可以直接拿来使用,我这里封装在map里,大家根据自己情况,也可以封装成一个对象使用,

public class ReadPropertiesUtils {/*** 获取配置文件的属性,封装为map* @return*/public static Map<String, Object> getProperties(){Map<String, Object> datas = new HashMap<String, Object>();InputStream ins = ReadPropertiesUtils.class.getClassLoader().getResourceAsStream("application.properties");Properties properties = new Properties();try {properties.load(ins);String username = properties.getProperty("username");String password = properties.getProperty("password");String url = properties.getProperty("url");String driverClassName = properties.getProperty("driverClassName");if(username != null && username != ""){datas.put("username", username);}if(password != null && password != ""){datas.put("password", password);}if(driverClassName != null && driverClassName != ""){datas.put("driverClassName", driverClassName);}if(url != null && url != ""){datas.put("url", url);}} catch (Exception e) {e.printStackTrace();System.out.println("读取异常");}finally{try {ins.close();} catch (IOException e) {e.printStackTrace();}}return datas;}public static void main(String[] args) {Map<String, Object> datas = ReadPropertiesUtils.getProperties();String username = (String) datas.get("username");System.out.println(username);}}

1、标准单例模式,代码如下,

public class CommonDbinfo {//普通的非静态的成员变量private String username;private String driverClassName;public String getUsername() {return username;}public String getDriverClassName() {return driverClassName;}/*** 唯一的全局对象,必须是static,这样创建一次后就直接存在内存中,确保垃圾回收器不回对此对象进行回收*/private static CommonDbinfo dbinfo;/*** 私有构造函数,避免外部类直接通过new创建对象*/private CommonDbinfo(){System.out.println("进入内部构造函数");}/*** 返回全局唯一的单例对象* 也是外部访问该单例对象的唯一入口* @return*/public static CommonDbinfo getInstance(){if(dbinfo == null){dbinfo = new CommonDbinfo();dbinfo.username = (String) ReadPropertiesUtils.getProperties().get("username");dbinfo.driverClassName = (String) ReadPropertiesUtils.getProperties().get("driverClassName");}return dbinfo;}}

需要注意的是,单例类中的构造函数不能是public,否则就失去了单例的意义了;

测试代码:

public class TestDbInfo {public static void main(String[] args) {CommonDbinfo dbinfo1 = CommonDbinfo.getInstance();System.out.println("第一次访问 :" + dbinfo1.getUsername());CommonDbinfo dbinfo2 = CommonDbinfo.getInstance();System.out.println("第二次访问 :" + dbinfo2.getUsername());}
}

运行这段代码,看控制台打印结果时候发现,如果多运行几次,发现第二次,第三次结果现实的速度都比第一次要快,说明单例模式的效果已经达到了;

2、懒汉模式,顾名思义,懒汉模式下,单例对象只要在第一次被调用的时候才会创建出来,由于懒汉模式的内部的实例化是包裹在static代码块中的,所以天然是线程安全的,下面看代码,

/*** 懒汉模式* 在第一次调用对象的时候才会创建出来* @author admin**/
public class LazySingleInfo {//普通成员变量,非静态的private String username;private String driverClassName;public String getUsername() {return username;}public String getDriverClassName() {return driverClassName;}/*** 唯一的全局对象,必须是static,这样创建一次后就直接存在内存中,确保垃圾回收器不回对此对象进行回收*/private static LazySingleInfo lazySingleInfo;/*** 懒汉模式只有在第一次调用的时候才会创建* 在多线程模式下,由于系统能够保证static 的代码块只执行一次,因此懒汉模式下的单例是线程安全的*/static {lazySingleInfo = new LazySingleInfo();lazySingleInfo.username = (String) ReadPropertiesUtils.getProperties().get("username");lazySingleInfo.driverClassName = (String) ReadPropertiesUtils.getProperties().get("driverClassName");}/*** 私有构造函数,避免外部类直接通过new创建对象*/private LazySingleInfo(){System.out.println("进入lazySingleInfo的内部构造函数");}/*** 返回全局唯一的单例对象* 也是外部访问该单例对象的唯一入口* @return*/public static LazySingleInfo getLayzInstance(){return lazySingleInfo;}}

测试类代码:

public class TestLazy {public static void main(String[] args) {LazySingleInfo info1 = LazySingleInfo.getLayzInstance();System.out.println("第一次访问 :" +info1.getUsername());System.out.println("第一次访问 :" +info1.getDriverClassName());LazySingleInfo info2 = LazySingleInfo.getLayzInstance();System.out.println("第二次访问 :" +info2.getDriverClassName());System.out.println("第二次访问 :" +info2.getUsername());}}

3、饿汉模式,饿汉模式很容易理解,就是单例对象一加载的时候就会被创建出来,很显然,这种模式下,初始化的对象时候需要使用static进行修饰,同时,由于饿汉模式是非线程安全的,为了确保在并发环境访问的情况下依然保证单例的有效性,需要使用synchronized进行控制,下面看代码:

/*** 饿汉模式在调用对象之前就已经创建出来* @author admin**/
public class HungrySingleInfo {// 普通成员变量,非静态的private String username;private String driverClassName;public String getUsername() {return username;}public String getDriverClassName() {return driverClassName;}/*** 对象加载的时候就将本对象创建出来*/private static HungrySingleInfo hungrySingleInfo = new HungrySingleInfo();/*** 私有构造函数,避免外部类直接通过new创建对象*/private HungrySingleInfo(){System.out.println("进入hungrySingleInfo的内部构造函数");}/*** 返回全局唯一的单例对象* 也是外部访问该单例对象的唯一入口* 添加synchronized之后,保证在多线程并发访问该单例对象的时候依然是只创建一个单例* @return*/public static HungrySingleInfo getHungryInstance(){if(hungrySingleInfo == null){//第一次如果高并发访问的时候,多个线程将在这里排队synchronized (hungrySingleInfo) {if(hungrySingleInfo == null){hungrySingleInfo = new HungrySingleInfo();hungrySingleInfo.username = (String) ReadPropertiesUtils.getProperties().get("username");hungrySingleInfo.driverClassName = (String) ReadPropertiesUtils.getProperties().get("driverClassName");}}}return hungrySingleInfo;}}

注意的是上面的代码中,两次使用hungrySingleInfo == null进行判断,目的很明显,第一次是判断单例对象是否存在,第二次是控制多线程并发访问的时候,如果多个线程在某个时刻同时通过额第一个判断,那么某个线程通过了synchronized 之后,仍然会继续寻找单例,如果存在了,就不会创建第二次,总体来说,就是确保多线程下,仍然知会创建一次

测试代码,

public class TestHungry {public static void main(String[] args) {HungrySingleInfo info3 = HungrySingleInfo.getHungryInstance();System.out.println("第一次访问 :" +info3.getDriverClassName());HungrySingleInfo info4 = HungrySingleInfo.getHungryInstance();System.out.println("第二次访问 :" +info4.getDriverClassName());}
}

本篇内容源代码:https://download.csdn.net/download/zhangcongyi420/10666210,有需要可以下载

有关单例模式的讲解,到此结束了,更多关于单例模式的深层的内容,小伙伴们可以自行继续深入研究,感谢观看!

java中单例模式用法详解相关推荐

  1. Java中SimpleDateFormat用法详解

    Java中怎么才能把日期转换成想要的格式呢,或把字符串转换成一定格式的日期,如把数据库中的日期或时间转换成自己想要的格式,JAVA中提供了SimpleDateFormat类可以实现,以下是Simple ...

  2. Java中 DecimalFormat 用法详解

    对Java中 DecimalFormat 的所有基础用法进行了一个汇总.DecimalFormat 类主要靠 # 和 0 两种占位符号来指定数字长度.0 表示如果位数不足则以 0 填充,# 表示只要有 ...

  3. Java中Map用法详解

    原文地址http://blog.csdn.net/guomutian911/article/details/45771621 原文地址http://blog.csdn.net/sunny2437885 ...

  4. String 在Java中的用法详解

    认识String类 和 String的使用 1.创建字符串: (1)常见的构造String的方式: (2)String的基本概念: 2.字符串比较相等: 3.字符.字节.字符串的转换 (1)字符与字符 ...

  5. java中ArrayList用法详解,基本用法(含增删改查)

    1.什么是ArrayList  ArrayList就是 动态数组,它提供了 ①动态的增加和减少元素  ②实现了ICollection和IList接口 ③灵活的设置数组的大小 ArrayList是一个其 ...

  6. Python中self用法详解

    Python中self用法详解 https://blog.csdn.net/CLHugh/article/details/75000104 首页 博客 学院 下载 图文课 论坛 APP 问答 商城 V ...

  7. Java中JDBC连接数据库详解

    今天动力节点java学院小编分享的是JDBC连接数据库的相关知识,希望通过看过此文,各位小伙伴对DBC连接数据库有所了解,下面就跟随小编一起来看看JDBC连接数据库的知识吧. 一.JDBC连接数据库概 ...

  8. pdo mysql limit_PHP mysql中limit用法详解(代码示例)

    在MySQL中,LIMIT子句与SELECT语句一起使用,以限制结果集中的行数.LIMIT子句接受一个或两个offset和count的参数.这两个参数的值都可以是零或正整数. offset:用于指定要 ...

  9. php 配置 error_reporting,PHP中error_reporting()用法详解 技术分享

    在php中error_reporting函数有什么作用? error_reporting([ int $level ] ) - 设置应该报告何种 PHP 错误. 该函数能够在运行时设置 error_r ...

最新文章

  1. BOYA 心形灵敏度麦克风的改装
  2. DNS原理及简单配置
  3. CentOS中安装git
  4. POJ - 2115 C Looooops(扩展欧几里得)
  5. 图像处理理论(五)——SIFT
  6. Even for transaction data request, metadata is still needed as prerequisite
  7. postman 不安全网站_接口工具分析(apipost、jmeter、postman)
  8. gram矩阵的性质_第十七课:正交矩阵和GramSchmidt正交化——MIT线性代数课程学习笔记...
  9. HTML语言的一些元素(二)
  10. 中国AI专利数稳居第一!世界各国AI专利深度盘点
  11. “虚度”一下时光,给老友写封信
  12. 《超越宝典汽配汽修管理系统——“美容管理”模块》项目研发阶段性总结
  13. 媒体查询之响应式布局
  14. matlab 正交多项式,求助~Matlab关于正交多项式
  15. css 外边距合并问题
  16. 32 Qt 之绘图之绘制一个漂亮的西瓜
  17. 【Unity入门计划】Unity2D动画(2)-脚本与混合树实现玩家角色动画过渡
  18. 清北学堂2019.8.8
  19. 程序员的工资是不是被高估了?
  20. 227 Entering Passive Mode (xxx,xxx,,xxx,xxx,x)

热门文章

  1. 第13-17章阅读后的思考
  2. DSS流媒体服务器搭建
  3. S2SH 第一次整合的经历
  4. 成都至柬埔寨金边定期直飞航线开通
  5. Android Lint 实践 —— 简介及常见问题分析
  6. 常用品牌交换机镜像抓包配置
  7. CSS3实现圆角效果
  8. Sql server安装时出现找不到vc_red.msi错误
  9. Android学习系列(41)--Android Studio简单使用
  10. Spring从菜鸟到高手(一)实现AOP的基本原理