单例模式---设计模式(一)
来源:互联网 发布:情景喜剧排行知乎 编辑:程序博客网 时间:2024/06/05 20:50
单例模式:确保一个类最多只有一个实例,并提供一个全局访问点
单例模式类图:
类图说明:
首先说明一下该类图中的符号:
类:
使用三层矩形框表示
第一层:显示类的名称,如果是抽象类,则就用斜体显示
第二层:字段和属性
第三层:类的方法
“+”:表示public
“-”:表示private
“#”:表示protected
接口:
使用两层矩形框表示,与类图的区别主要是顶端有<<interface>>
显示
第一层:接口的名称
第二层:接口的方法
继承类(extends):
用空心三角形+直线表示
实现接口(implements):
用空心三角形+虚线表示
关联(Association):
用实线箭头来表示
聚合(Aggregation):
用空心菱形+实线箭头来表示
聚合:表示一种弱的‘拥有’关系,体现的是A对象可以包含B,但B对象不是A对象的一部分,例如:公司和员工
组合(Composition):
用实心菱形+实线箭头来表示
组合:部分和整体的关系,并且生命周期是相同的。例如:人和手
依赖(Dependency):
用虚线箭头来表示
例如:动物和氧气
单例模式
Singleton类称为单例类,通过使用private的构造函数,确保了在一个应用中只产生一个实例,并且是自行实例化的(在Singleton中自己使用new Singleton())。
单例模式的通用代码:
饿汉式:
package singletontest;/** * 单例模式(饿汉模式) * 应用场合:有些对象只需要一个就足够了,如古代皇帝,老婆 * 作用:保证整个应用程序中某个实例有且只有一个 * 类型:懒汉模式,饿汉模式 * @author 张耀晖 * */public class Singleton { //1.将构造方法私有化,不允许外部直接创建对象 private Singleton(){ } //2.创建类的唯一实例,使用private static修饰 private static Singleton singleton = new Singleton(); //3.提供一个用于获取实例的方法,使用public static修饰 public static Singleton getInstance(){ return singleton; }}
懒汉式:
package singletontest;/** * 懒汉模式 * 区别:饿汉模式的特点是加载类时比较慢,但运行时获取对象的速度比较快,线程安全 * 懒汉模式特点是加载类时比较快,但运行时获取对象速度比较慢,线程不安全 * @author 张耀晖 * */public class Singleton2 { //1.将构造方法私有化,不允许外边直接创建对象 private Singleton2(){ } //2.声明类的唯一实例,使用private static修饰 private static Singleton2 singleton2; //3.提供一个用于获取实例的方法,使用public static修饰 public static Singleton2 getInstance(){ if(singleton2==null){ singleton2 = new Singleton2(); } return singleton2; }}
package singletontest;public class Test { public static void main(String[] args) { //饿汉模式 Singleton s1 = Singleton.getInstance(); Singleton s2 = Singleton.getInstance(); if(s1==s2){ System.out.println("s1和s2是同一个实例"); }else{ System.out.println("s1和s2不是同一个实例"); } //懒汉模式 Singleton2 s3 = Singleton2.getInstance(); Singleton2 s4 = Singleton2.getInstance(); if(s3==s4){ System.out.println("s3和s4是同一个实例"); }else{ System.out.println("s3和s4不是同一个实例"); } }}
运行结果:
注意:
懒汉式是存在线程不安全问题的。
懒汉式在低并发的情况下有可能不会出现问题,但是当高并发的情况下则可能在内存中出现多个实例。
eg:如果一个线程A执行到singleton2 = new Singleton2(),但还没有获得对象(对象初始化是需要时间的),第二个线程B也在执行,执行到singleton2==null的判断,那么线程B获得判断条件也是为真,于是继续运行下去,线程A获得一个对象,线程B也获得一个对象,在内存中就出现了两个对象。
解决懒汉式的线程不安全问题的方法:
在getInstance()方法前加synchronized关键字。不过这样的话只适应调用实例对象少的情况,如果调用实例对象比较频繁synchronized就会影响系统的执行效率。那么就建议使用饿汉式。
饿汉式也有一定的问题,在不使用类的实例对象的时候也会创建该类的实例对象,造成一定的资源浪费。
单例模式的优点
•由于单例模式在内存中只有一个实例,减少了内存开支,特别是一个对象需要频繁的被创建、销毁,而且创建或销毁时性能又无法优化,单例模式的优势就非常明显;
•由于单例模式只生成一个实例,减少了系统性能开销,当一个对象的产生需要比较多的资源时,如读取配置、产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后永久驻留内存的方式来解决(在Java EE中采用单例模式时需要注意JVM垃圾回收机制);
•单例模式可以避免对资源的多重占用,例如一个写文件动作,由于只有一个实例存在内存中,避免对同一个资源文件的同时写操作。
•单例模式可以在系统设置全局的访问点,优化环共享资源访问,例如可以设计一个单例类,负责所有数据表的映射处理。单例模式的缺点
•单例模式没有接口,扩展很困难,若要扩展,除了修改代码没有第二种途径可以实现。单例模式为什么不能增加接口呢?因为接口对单例模式是没有任何的意义,它要求“自行实例化”,并且提供单一实例、接口或抽象类是不可能被实例化的。
•单例模式对测试是不利的。在并行开发环境中,如果单例模式没有完成,是不能进行测试的,没有接口也不能使用mock的方式虚拟一个对象。
•单例模式与单一职责原则有冲突。一个类应该只实现一个的逻辑,而不关心它是否是单例的,决定它是不是要单例是环境决定的,单例模式把“要单例”和业务逻辑融合也在一个类中。单例模式的使用场景
在一个系统中,要求一个类有且仅有一个对象,如果出现多个对象就会出现“不良反应”时,则可以采用单例模式,具体的场景如下:
•要求生成唯一序列号的环境;
•在整个项目中需要有访问一个共享访问点或共享数据,例如一个Web页面上的计数器,可以不用每次刷新都记录到数据库中,使用单例模式保持计数器的值,并确保是线程安全的;
•创建一个对象需要消耗的资源过多,如要访问IO、访问数据库等资源;
•需要定义大量的静态常量和静态方法(如工具类)的环境,可以采用单例模式(当然,也可以直接声明为static的方式);
单例模式的扩展
现在有一个问题:如果要求一个类只能产生固定数量(大于1个)的对象怎么办?
package com.test.singletonTest;import java.util.ArrayList;import java.util.Random;/** * 单例模式的扩展,产生固定数量的(大于1个)的实例对象 * 2015年9月17日 下午5:08:15 * @author 张耀晖 * */public class SingletonTest { private static int maxnum = 3;//定义最多能产生的实例对象的数量 private static ArrayList<SingletonTest> singletonList = new ArrayList<SingletonTest>();//定义一个集合,容纳所有的实例对象 private static ArrayList<String> singletonNameList = new ArrayList<String>();//定义一个集合,容纳所有对象的私有属性 //当前实例对象的标号 private static int countOfNum; //产生所有的对象 static{ for (int i = 0; i < maxnum; i++) { singletonList.add(new SingletonTest("实例对象:"+i+1)); } }// //将类中默认的无参构造方法重写为private,防止在外部创建该类的实例对象// private SingletonTest(){// // } //传入实例对象的名称,创建实例对象 private SingletonTest(String name){ singletonNameList.add(name); } //随机获得一个实例对象 public static SingletonTest getInstance(){ Random random = new Random(); countOfNum = random.nextInt(maxnum); //随机取出一个实例对象 SingletonTest singletonTest = singletonList.get(countOfNum); return singletonTest; } //获取实例对象的私有属性 public static String getSingletonName(){ String name = singletonNameList.get(countOfNum); return name; }}
- 设计模式(一) 单例模式
- 设计模式(一):单例模式
- 单例模式---设计模式(一)
- 设计模式(一):单例模式
- 设计模式(一)-单例模式
- 设计模式(一):单例模式
- 设计模式(一)单例模式
- 设计模式(一)单例模式
- 设计模式-单例模式(一)
- 设计模式(一):单例模式
- 设计模式(一)单例模式
- 设计模式(一)--单例模式
- 设计模式(一):单例模式
- 设计模式(一)-单例模式
- 设计模式(一) 单例模式
- 设计模式(一)--单例模式
- 设计模式(一):单例模式
- javascript设计模式(一)单例模式 策略模式
- STL之vector的实现
- 算法学习--归并排序
- LSTM模型
- 深入理解C++
- 规范命名
- 单例模式---设计模式(一)
- Git入门教程
- 黑马程序员_java学习笔记 5.java面向对象特性
- How does Spring know that ThrowsAdvice.afterThrowing needs to be called?
- java学习2-循环中的嵌套语句2
- Spring与Quartz的整合实现定时任务调度
- iPhone如何查询序列号 iPhone激活时间查询
- javascript深入理解js闭包
- mac10.10Apache配置笔记