设计模式 — 单例模式(Singleton)
在一个软件系统中,经常有有些特殊的对象就需要一个实例,如果有多个的话,就比较浪费服务器资源,最典型的就是 整个系统的配置文件对象。
普通方式读取配置文件
// 配置文件 SingletonApp.properies paramA2 = AAAAAA
paramB2 =BBBBBBBBBBB /*************** AppConfig **************************************/ package designPattern_1_Singleton; import java.io.IOException;
import java.io.InputStream;
import java.util.Properties; public class AppConfig {
private String ParamA;
private String ParamB; public String getParamA() {
return ParamA;
} public String getParamB() {
return ParamB;
} public AppConfig()
{
readConfig();
} private void readConfig()
{
Properties prop = new Properties();
InputStream in = null;
try{
in = this.getClass().getResourceAsStream("SingletonApp.properties"); prop.load(in);
this.ParamA = prop.getProperty("paramA2");
this.ParamB = prop.getProperty("paramB2"); }
catch(IOException e){
System.out.println("加载配置文件出错了,详细信息参考以下");
e.printStackTrace();
}
finally{
try {
in.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
};
}
} } /******************** Client.java *****************************/
package designPattern_1_Singleton; public class Client { public static void main(String[] args) {
// TODO Auto-generated method stub AppConfig app = new AppConfig(); String paramA = app.getParamA();
String paramB = app.getParamB(); System.out.println("paramA = " + paramA + " paramB = " + paramB); } }
不用单例模式实现
上面是不用单例模式实现的功能。那么这段代码有什么问题吗?在实现功能方面来说,是完成了功能,但是设计不够合理,这样做没用一次,就要 new 一个config 类。而且发现所有的 Config 类内容都是一样的。这样的话,就存在浪费资源的,这样就有了优化的方面。
优化的内容就是所有需要用到配置的内容都公用一个 Config 类,这样的话就大大节省了资源。从这个功能的描述我们再抽象一下,就引出了单例模式,下面我们来看一下单例模式的定义:
单例模式:保证一个类仅有一个实例,并且提供一个它的全局访问点。
这样就要保证在整个系统中保证需要用单例模式的类的构造函数只能执行一次。根据这个思路把上面的例子用单例模式改造一下。
package designPattern_1_Singleton; import java.io.IOException;
import java.io.InputStream;
import java.util.Properties; public class SingletonAppConfig { /**
* 定义一个静态变量,来存储创建的类的实例,因为是静态的,所以只能创建一次。
*/
private static SingletonAppConfig instance = new SingletonAppConfig(); /**
* 用来提供外界实例类的函数,因为构造函数私有了,那么返回存放 实例的变量的方式来床创建类。
* @return SingletonAppConfig 的实例
*/
public static SingletonAppConfig getInstance(){
return instance;
} //为了只有一个实例,把构造函数改成私有
private SingletonAppConfig()
{
readConfig();
} private String ParamA;
private String ParamB; public String getParamA() {
return ParamA;
} public String getParamB() {
return ParamB;
} private void readConfig()
{
Properties prop = new Properties();
InputStream in = null;
try{
in = this.getClass().getResourceAsStream("SingletonApp.properties"); prop.load(in);
this.ParamA = prop.getProperty("paramA2");
this.ParamB = prop.getProperty("paramB2"); }
catch(IOException e){
System.out.println("加载配置文件出错了,详细信息参考以下");
e.printStackTrace();
}
finally{
try {
in.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
};
}
}
} ***************************************************************** package designPattern_1_Singleton; public class Client2 { public static void main(String[] args) {
// TODO Auto-generated method stub
SingletonAppConfig sConfig = SingletonAppConfig.getInstance();
for(int i=0;i<=300;i++)
{
sConfig = SingletonAppConfig.getInstance();
System.out.println(sConfig);
}
System.out.println(sConfig.getParamA()+" **** "+sConfig.getParamB());
}
} **************************************************************
designPattern_1_Singleton.SingletonAppConfig@4e8a88a9
designPattern_1_Singleton.SingletonAppConfig@4e8a88a9
designPattern_1_Singleton.SingletonAppConfig@4e8a88a9
designPattern_1_Singleton.SingletonAppConfig@4e8a88a9
designPattern_1_Singleton.SingletonAppConfig@4e8a88a9
designPattern_1_Singleton.SingletonAppConfig@4e8a88a9
designPattern_1_Singleton.SingletonAppConfig@4e8a88a9
designPattern_1_Singleton.SingletonAppConfig@4e8a88a9
designPattern_1_Singleton.SingletonAppConfig@4e8a88a9
designPattern_1_Singleton.SingletonAppConfig@4e8a88a9
designPattern_1_Singleton.SingletonAppConfig@4e8a88a9
designPattern_1_Singleton.SingletonAppConfig@4e8a88a9
designPattern_1_Singleton.SingletonAppConfig@4e8a88a9
designPattern_1_Singleton.SingletonAppConfig@4e8a88a9
AAAAAA **** BBBBBBBBBBB
Singleton_1
通过以上代理,也实现了读取配置文件的功能,但是明显修改后性能更好,因为修改后,这个类在系统中无论在什么地方调用,都只能实现一个实例,能够节省很多性能,改造后的就是单例模式。
单例模式整体上有两种实现方式,一种就是上面的这种,饿汉模式,另外一种是懒汉模式。
那么怎么区分饿汉模式和懒汉模式呢,上面实现的是饿汉模式,下面贴出来懒汉模式,然后在区分下:
package designPattern_1_Singleton; import java.io.IOException;
import java.io.InputStream;
import java.util.Properties; /*
* 懒汉式实现办法
*/
public class Singleton2AppConfig { private static Singleton2AppConfig s2Config = null; public Singleton2AppConfig getInstance(){
//判断是否创建了实例,如果没有则创建
if(s2Config == null){
s2Config = new Singleton2AppConfig();
}
return s2Config;
} private Singleton2AppConfig(){
readConfig();
} private String ParamA;
private String ParamB; public String getParamA() {
return ParamA;
} public String getParamB() {
return ParamB;
} private void readConfig()
{
Properties prop = new Properties();
InputStream in = null;
try{
in = this.getClass().getResourceAsStream("SingletonApp.properties"); prop.load(in);
this.ParamA = prop.getProperty("paramA2");
this.ParamB = prop.getProperty("paramB2"); }
catch(IOException e){
System.out.println("加载配置文件出错了,详细信息参考以下");
e.printStackTrace();
}
finally{
try {
in.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
};
}
}
}
Singleton_2
通过 Singleton_1 与 Singleton_2 比较,两个模式的本质区别是:创建对象实例的先后。
饿汉式单例模式,形象的说就是很饿,要急吼吼的创建实例,急到加载类的时候就创建了实例对象。关键代码是
/**
* 定义一个静态变量,来存储创建的类的实例,因为是静态的,所以只能创建一次。
*/
private static SingletonAppConfig instance = new SingletonAppConfig();
懒汉式单例模式,很实例的创建拖拖拉拉,能不创建就不创建。直到需要使用的时候再去进行了创建,关键代码是:
private static Singleton2AppConfig s2Config = null; public static Singleton2AppConfig getInstance(){
//判断是否创建了实例,如果没有则创建
if(s2Config == null){
s2Config = new Singleton2AppConfig();
}
return s2Config;
}
通过以上要点总结下单例模式的要点:
1、因为类的性质,他所有实例都是相同的,因此控制这个类只能有一个实例对象。
2、定义一个私有静态变量,存放实例后的类,因为是静态变量。
3、类的改造函数设置为私有,不让外界通过构造函数创建实例对象。
4、但是使用类总是要实例的,所以就提供一个共有的静态方法来创建实例对象。但是为了让类能够使用这个方法创建对象,所以这个类必须要是静态方法。
5、通过提供的静态方法(一般都是 getInstance)来创建类实例(因为普通方法需要先实例后才能调用,所以这个方法必须是静态方法)。
懒汉式和饿汉式模式区别
1、从时间和空间方面来说,懒汉式是因为用的时候才创建实例,而且在每次创建类的时候都需要判断,所以是时间空间模式。而饿汉式模式是每次装载类的时候就要进行实例,因此是空间换时间。
2、从线程安全方面来讲。饿汉式因为是加载类的时候就实例化了,而虚拟机保证加载类只会加载一次,因此是线程安全的。但是装载类和实例类不同步的懒汉式则是不安全的。
private static Singleton2AppConfig s2Config = null; public static Singleton2AppConfig getInstance(){
//判断是否创建了实例,如果没有则创建
if(s2Config == null){
s2Config = new Singleton2AppConfig();
} return s2Config;
}
这个问题就是要用加锁的方式来解决
/**
* volatile 修改符保证这个类每次都是执行 volatile
*/
private volatile static Singleton2AppConfig s2Config = null; public static Singleton2AppConfig getInstance(){
//判断是否创建了实例,如果没有则创建
if(s2Config == null){
synchronized (Singleton2AppConfig.class) {
s2Config = new Singleton2AppConfig();
}
} return s2Config;
}
加锁后由于只是在第一次创建类使用同步锁功能,因此影响也不大。
实例模式的变种,可能创建指定数量的实例
单例模式的本质是控制类实例化的数量,那么如果单例的这个类如果使用频率高到一个实例不够使用了,那么是否可以达到指定需要的实例数量呢?这个是可以的。实例化有一定的数量后,调用肯定也是有特殊的算法,这里我们不探究调用的算法,只看如果实现指定数量的类实例。看代码:
package designPattern_1_Singleton; import java.util.HashMap;
import java.util.Map; public class Singleton3AppConfig {
private final static String CACHE = "cache"; private static Map<String, Singleton3AppConfig> map = new HashMap<String, Singleton3AppConfig>(); private static int MAX = 3; private static int num = 1; private Singleton3AppConfig() { } public static Singleton3AppConfig getInstance() {
String classkey = CACHE + num;
if (map.get(classkey) == null) { map.put(classkey, new Singleton3AppConfig());
}
num += 1;
if (num > MAX) {
num = 1;
} return map.get(classkey);
}
} /***********************8*************************************/ package designPattern_1_Singleton; public class Client3 { public static void main(String[] args) {
// TODO Auto-generated method stub
for(int i=0;i<30;i++){
Singleton3AppConfig s3c = Singleton3AppConfig.getInstance();
System.out.println(s3c);
} } } /***********************8*************************************/ designPattern_1_Singleton.Singleton3AppConfig@5f4fcc96
designPattern_1_Singleton.Singleton3AppConfig@7000bcbc
designPattern_1_Singleton.Singleton3AppConfig@40671416
designPattern_1_Singleton.Singleton3AppConfig@5f4fcc96
designPattern_1_Singleton.Singleton3AppConfig@7000bcbc
designPattern_1_Singleton.Singleton3AppConfig@40671416
designPattern_1_Singleton.Singleton3AppConfig@5f4fcc96
designPattern_1_Singleton.Singleton3AppConfig@7000bcbc
designPattern_1_Singleton.Singleton3AppConfig@40671416
designPattern_1_Singleton.Singleton3AppConfig@5f4fcc96
designPattern_1_Singleton.Singleton3AppConfig@7000bcbc
designPattern_1_Singleton.Singleton3AppConfig@40671416
designPattern_1_Singleton.Singleton3AppConfig@5f4fcc96
designPattern_1_Singleton.Singleton3AppConfig@7000bcbc
designPattern_1_Singleton.Singleton3AppConfig@40671416
designPattern_1_Singleton.Singleton3AppConfig@5f4fcc96
designPattern_1_Singleton.Singleton3AppConfig@7000bcbc
designPattern_1_Singleton.Singleton3AppConfig@40671416
designPattern_1_Singleton.Singleton3AppConfig@5f4fcc96
designPattern_1_Singleton.Singleton3AppConfig@7000bcbc
designPattern_1_Singleton.Singleton3AppConfig@40671416
designPattern_1_Singleton.Singleton3AppConfig@5f4fcc96
designPattern_1_Singleton.Singleton3AppConfig@7000bcbc
designPattern_1_Singleton.Singleton3AppConfig@40671416
designPattern_1_Singleton.Singleton3AppConfig@5f4fcc96
designPattern_1_Singleton.Singleton3AppConfig@7000bcbc
designPattern_1_Singleton.Singleton3AppConfig@40671416
designPattern_1_Singleton.Singleton3AppConfig@5f4fcc96
designPattern_1_Singleton.Singleton3AppConfig@7000bcbc
designPattern_1_Singleton.Singleton3AppConfig@40671416
这个是单例模式变种,代码很好理解。这儿看执行效果,虽然调用的地方实例了30个,但是从打印内容来看,只有三个 实例化的地址。所以知识实例化了指定的三个实例。(这里补充一点:如果构造函数没有返回一个字符串,那么如果直接输出类的话,就输出的是他的地址)
小结
单例模式是整个设计模式中最好理解,也是在使用场景中最容易分辨使用的设计模式。使用单例模式不会因为使用设计模式让代码变得较复杂。而他的目的就是为了在让合适的类只有一个实例对象,从而达到节省资源开支的目的。
设计模式 — 单例模式(Singleton)的更多相关文章
- 设计模式 单例模式(Singleton) [ 转载2 ]
设计模式 单例模式(Singleton) [ 转载2 ] @author java_my_life 单例模式的结构 单例模式的特点: 单例类只能有一个实例. 单例类必须自己创建自己的唯一实例. 单例类 ...
- 设计模式 单例模式(Singleton) [ 转载 ]
设计模式 单例模式(Singleton) [ 转载 ] 转载请注明出处:http://cantellow.iteye.com/blog/838473 前言 懒汉:调用时才创建对象 饿汉:类初始化时就创 ...
- JAVA设计模式-单例模式(Singleton)线程安全与效率
一,前言 单例模式详细大家都已经非常熟悉了,在文章单例模式的八种写法比较中,对单例模式的概念以及使用场景都做了很不错的说明.请在阅读本文之前,阅读一下这篇文章,因为本文就是按照这篇文章中的八种单例模式 ...
- 浅谈设计模式--单例模式(Singleton Pattern)
题外话:好久没写blog,做知识归纳整理了.本来设计模式就是个坑,各种文章也写烂了.不过,不是自己写的东西,缺少点知识的存在感.目前还没做到光看即能记住,得写.所以准备跳入设计模式这个大坑. 开篇先贡 ...
- [工作中的设计模式]单例模式singleton
一.模式解析: 单例模式是最简单和最常用的设计模式,面试的时候,不管新毕业的学生还是已经工作多年的筒子,对单例模式基本都能聊上两句.单例模式主要体现在如下方面: 1.类的构造函数私有化,保证外部不能直 ...
- 23种设计模式--单例模式-Singleton
一.单例模式的介绍 单例模式简单说就是掌握系统的至高点,在程序中只实例化一次,这样就是单例模式,在系统比如说你是该系统的登录的第多少人,还有数据库的连接池等地方会使用,单例模式是最简单,最常用的模式之 ...
- 设计模式--单例模式Singleton(创建型)
单例模式很显然是定义一个类,这个类在程序中只有唯一的实例对象.一般单例类的构造函数是私有的,只能通过调用静态函数GetInstance来获取实例. 一.单例模式有三种:懒汉式单例.饿汉式单例.登记式单 ...
- 设计模式--单例模式Singleton
单例模式顾名思义整个程序下只有一个实例,例如一个国家只有一个皇帝,一个军队只有一个将军.单例模式的书写又分为饿汉模式和懒汉模式 饿汉模式 类中代码 package demo; public cla ...
- 设计模式——单例模式(Singleton)
保证一个类仅有一个实例,并提供一个访问它的全局访问点.——DP UML类图 模式说明 个人认为单例模式是所有设计模式中最为简单的一个模式,因为实现这个模式仅需一个类,而不像其他模式需要若干个类.这个模 ...
- 设计模式-单例模式(Singleton) (创建型模式)
//以下代码来源: 设计模式精解-GoF 23种设计模式解析附C++实现源码 //Singleton.h #pragma once #include<iostream> class Sin ...
随机推荐
- 查看macOS下正在使用的zsh
使用dscl . -read /Users/$USER UserShell查看 如果你的结果是/bin/zsh,又恰巧用brew安装了zsh的话,那么你可能就白安装了 将brew安装的zsh添加到/e ...
- Security+ 认证考过经验分享 802分飘过
PART 1/考前准备 1.针对与新人.学生建议看每一节直播课程,老师会结合自己的工作工作经验讲解课程,可以帮助学生理解知识. 2.备考期间建议官方指导手册至少看两遍以上,我在结合自己的做题库时发现有 ...
- 结构体addrinfo, sockaddr, sockaddr_in的区别
struct sockaddr和struct sockaddr_in这两个结构体用来处理网络通信的地址. 一.sockaddr sockaddr在头文件#include <sys/socket. ...
- 转载:在做datatable时候查询数据和条数只用一次sql就可以解决需求
前言:最近用datatable处理数据比较多,所以在使用时候想提升性能 select * from t_hr_leave SELECT FOUND_ROWS() //返回查询记录的总数 select ...
- Jupyter Notebook 的安装使用以及 tree 路径变更
由于最近开始学习 Python,进而接触到一个十分强大的交互式编辑器 — Jupyter Notebook,用起来也非常顺手,于是记录一下相关的使用过程. 一.安装 Python: ①首先前往 pyt ...
- 题解 P3871 【[TJOI2010]中位数】
orz各位大佬,题解太强了,主席树,堆,线段树,splay,还有暴力,太巨了.所以我用的是fhq treap(好像更高级).算了. 反正都是平衡树,这道题就是动态求中位数,不会做的同学可以先做弱化版P ...
- windows查看进程信息
wmic process where caption="java.exe" get processid,caption,commandline /value
- VS发布网站时,报错提示:“未能将文件xxx复制到xxx,未能找到文件xx”三种解决方案!
发布网站时候大家可能会遇到这样的情况,就是报错提示说:“未能将文件xxx复制到xxx,未能找到文件xx”,这个问题一般来说有三种解决方案,个人倾向第三种,如图: 解决方案如下: 方案一.把系统提示缺失 ...
- spring.http.multipart.maxFileSize提示无效报错问题处理
在SpringBoot项目中,配置spring.http.multipart.maxFileSize用于限定最大文件上传大小. 但是,SpringBoot版本不同,关于这一块的配置也不相同. 1.Sp ...
- java笔试要点(java.sql包)
提供JAVA存取数据库能力的包是 ( ) A: java.sql B: java.awt C: java.lang D: java.swing 解析: A,java.sql包提供Java存取数据库能力 ...