设计模式 单例模式
来源:互联网 发布:淘宝返利网站 编辑:程序博客网 时间:2024/06/08 17:39
1 场景问题
1.1 读取配置文件的内容
考虑这样一个应用,读取配置文件的内容。
很多应用项目,都有与应用相关的配置文件,这些配置文件多是由项目开发人员自定义的,在里面定义一些应用需要的参数数据。当然在实际的项目中,这种配置文件多采用xml格式的。也有采用properties格式的,毕竟使用Java来读取properties格式的配置文件比较简单。
现在要读取配置文件的内容,该如何实现呢?
1.2 不用模式的解决方案
有些朋友会想,要读取配置文件的内容,这也不是个什么困难的事情,直接读取文件的内容,然后把文件内容存放在相应的数据对象里面就可以了。真的这么简单吗?先实现看看吧。
为了示例简单,假设系统是采用的properties格式的配置文件
1.那么直接使用Java来读取配置文件,示例代码如下:
public class AppConfig { /** * 参数A */ private String paramterA; /** * 参数B */ private String paramterB; public String getParamterA() { return paramterA; } public String getParamterB() { return paramterB; } /** * 构造方法从配置文件中读取参数 */ public AppConfig() { readConfig(); } private void readConfig() { InputStream in = null; try { in = AppConfig.class.getResourceAsStream("config.properties"); Properties p = new Properties(); p.load(in); this.paramterA = p.getProperty("paramterA"); this.paramterB = p.getProperty("paramterB"); } catch (Exception e) { System.out.println("装载配置文件出错了,具体堆栈信息如下:"); e.printStackTrace(); } finally { try { in.close(); } catch (IOException e) { e.printStackTrace(); } } }}
- 客户端调用
public static void main(String[] args) { AppConfig ap = new AppConfig(); System.out.println(ap.getParamterA()); System.out.println(ap.getParamterB());}
1.3 有何问题
看看客户端使用这个类的地方,是通过new一个AppConfig的实例来得到一个操作配置文件内容的对象。如果在系统运行中,有很多地方都需要使用配置文件的内容,也就是很多地方都需要创建AppConfig这个对象的实例。
换句话说,在系统运行期间,系统中会存在很多个AppConfig的实例对象,这有什么问题吗?
当然有问题了,试想一下,每一个AppConfig实例对象,里面都封装着配置文件的内容,系统中有多个AppConfig实例对象,
也就是说系统中会同时存在多份配置文件的内容,这会严重浪费内存资源。
在一个系统运行期间,某个类只需要一个类实例就可以了,那么应该怎么实现呢?
2 解决方案
2.1 单例模式来解决
用来解决上述问题的一个合理的解决方案就是单例模式。那么什么是单例模式呢?
定义:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
首要的问题就是要把创建实例的权限收回来,让类自身来负责自己类实例的创建工作,
然后由这个类来提供外部可以访问这个类实例的方法,这就是单例模式的实现方式。
2.2 单例模式示例代码
单例模式的实现又分为两种,一种称为懒汉式,一种称为饿汉式,其实就是在具体创建对象实例的处理上,有不同的实现方式。下面分别来看这两种实现方式的代码示例。
1.懒汉式实现,示例代码如下:
public class Singleton { private static Singleton instance; /** * 私有化构造方法 */ private Singleton() { } /** * 定义一个方法来为客户端提供类实例 */ public static synchronized Singleton getInstance() { if (instance == null) { return new Singleton(); } return instance; } /** * 示意方法,单例可以有自己的操作 */ public void singletonOperation(){ //功能处理 } /** * 示意属性,单例可以有自己的属性 */ private String singletonData; /** * 示意方法,让外部通过这些方法来访问属性的值 * * @return 属性的值 */ public String getSingletonData(){ return singletonData; }}
2.饿汉式
public class Singleton { private static Singleton instance = new Singleton(); /** * 私有化构造方法 */ private Singleton() { } /** * 定义一个方法来为客户端提供类实例 */ public static synchronized Singleton getInstance() { return instance; } /** * 示意方法,单例可以有自己的操作 */ public void singletonOperation(){ //功能处理 } /** * 示意属性,单例可以有自己的属性 */ private String singletonData; /** * 示意方法,让外部通过这些方法来访问属性的值 * * @return 属性的值 */ public String getSingletonData(){ return singletonData; }}
两者的区别:
懒汉式是典型的时间换空间,
也就是每次获取实例都会进行判断,看是否需要创建实例,费判断的时间,当然,如果一直没有人使用的话,那就不会创建实例,节约内存空间。
饿汉式是典型的空间换时间,
当类装载的时候就会创建类实例,不管你用不用,先创建出来,然后每次调用的时候,就不需要再判断了,节省了运行时间。
2.3使用单例模式重写示例
1.懒汉式
public class AppConfig { private static AppConfig instance = null; /** * 参数A */ private String paramterA; /** * 参数B */ private String paramterB; public String getParamterA() { return paramterA; } public String getParamterB() { return paramterB; } /** * 构造方法私有化 */ private AppConfig() { readConfig(); } public static synchronized AppConfig getInstance() { if (instance == null) { return new AppConfig(); } return instance; } public void readConfig() { InputStream in = null; try { in = AppConfig.class.getResourceAsStream("config.properties"); Properties p = new Properties(); p.load(in); this.paramterA = p.getProperty("paramterA"); this.paramterB = p.getProperty("paramterB"); } catch (Exception e) { System.out.println("装载配置文件出错了,具体堆栈信息如下:"); e.printStackTrace(); } finally { try { in.close(); } catch (IOException e) { e.printStackTrace(); } } }}
2.饿汉式
public class AppConfig { private static AppConfig instance = new AppConfig(); /** * 参数A */ private String paramterA; /** * 参数B */ private String paramterB; public String getParamterA() { return paramterA; } public String getParamterB() { return paramterB; } /** * 构造方法私有化 */ private AppConfig() { readConfig(); } public static AppConfig getInstance() { return instance; } public void readConfig() { InputStream in = null; try { in = AppConfig.class.getResourceAsStream("config.properties"); Properties p = new Properties(); p.load(in); this.paramterA = p.getProperty("paramterA"); this.paramterB = p.getProperty("paramterB"); } catch (Exception e) { System.out.println("装载配置文件出错了,具体堆栈信息如下:"); e.printStackTrace(); } finally { try { in.close(); } catch (IOException e) { e.printStackTrace(); } } }}
3.客户端
public static void main(String[] args) { System.out.println(AppConfig.getInstance().getParamterA()); System.out.println(AppConfig.getInstance().getParamterB());}
3.在Java中一种更好的单例实现方式
public class SingleTon { private SingleTon() { } /** * 类级的内部类,也就是静态的成员式内部类,该内部类的实例与外部类的实例 没有绑定关系,而且只有被调用到才会装载,从而实现了延迟加载 */ private static class SingleTonHandler { /** * 静态初始化器,由JVM来保证线程安全 */ private static SingleTon instance = new SingleTon(); } public static SingleTon getInstance() { return SingleTonHandler.instance; } /** * 当getInstance方法第一次被调用的时候,它第一次读取SingletonHolder.instance, * 导致SingletonHolder类得到初始化;而这个类在装载并被初始化的时候,会初始化它的静态域, * 从而创建Singleton的实例,由于是静态的域,因此只会被虚拟机在装载类的时候初始化一次, * 并由虚拟机来保证它的线程安全性。 */}
4.枚举的单例实现
按照《高效Java 第二版》中的说法:单元素的枚举类型已经成为实现Singleton的最佳方法。
public enum Singleton { instacne; /** * 示意方法,单例可以有自己的操作 */ public void singletonOperation(){ //功能处理 } }
使用枚举来实现单实例控制,会更加简洁,而且无偿的提供了序列化的机制,
并由JVM从根本上提供保障,绝对防止多次实例化,是更简洁、高效、安全的实现单例的方式。
5.何时选用单例模式
当需要控制一个类的实例只能有一个,而且客户只能从一个全局访问点访问它时,可以选用单例模式,
这些功能恰好是单例模式要解决的问题。
- 设计模式------单例模式
- 设计模式------单例模式
- 设计模式-单例模式
- 设计模式 - 单例模式
- 设计模式---单例模式
- 设计模式---单例模式
- 【设计模式】单例模式
- 设计模式-单例模式
- 设计模式----单例模式
- 设计模式--单例模式
- 设计模式-单例模式
- 设计模式-单例模式
- [设计模式] 单例模式
- 设计模式--单例模式
- 设计模式---单例模式
- 设计模式--单例模式
- 设计模式 -----单例模式
- 设计模式:单例模式
- kafka->spark->streaming->mysql(scala)实时数据处理案列
- cocoapods相关的linux命令
- 云服务器Centos6.8安装配置samba文件共享
- java-无法新建servers的问题
- 如何写SysV服务管理脚本
- 设计模式 单例模式
- 【翻译】VTK 8.0.0
- 记:为何我设置了session为永久保存,但是并没有什么卵用
- 线段树
- GMS认证送测前自检项小结
- Toolbar详解 · Material Design Part 2
- Redis数据类型--Hash(哈希表)
- myeclipse安装svn插件无法显示问题解决
- codeblocks常用快捷键和设置