韩老师设计模式2:9种单例模式(含CAS示例)饿汉,懒汉,双重检查,枚举,内部类。UML类图:依赖,泛化,实现,关联,聚合,组合

基本介绍

  • 装饰模式:解决 类爆炸

<<设计模式>> 是经典的书,作者是 Erich Gamma、 Richard Helm、 Ralph
Johnson 和 John Vlissides Design(俗称 “四人组 GOF”)

分为三种类型,共23种

  1. 创建型模式: 单例模式、抽象工厂模式、原型模式、建造者模式、 工厂模式。

  2. 结构型模式:适配器模式、桥接模式、 装饰模式、组合模式、外观模式、享
    元模式、 代理模式。

  3. 行为型模式:模版方法模式、命令模式、访问者模式、迭代器模式、 观察者
    模式、中介者模式、备忘录模式、解释器模式(Interpreter模式)、状态模
    式、策略模式、职责链模式(责任链模式)。

单例

所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类
只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法(静态方法)。

比如Hibernate的SessionFactory,它充当数据存储源的代理,并负责创建Session
对象。 SessionFactory并不是轻量级的,一般情况下,一个项目通常只需要一个
SessionFactory就够,这是就会使用到单例模式。

八种方式:

  1. 饿汉式(静态常量)

  2. 饿汉式(静态代码块)

  3. 懒汉式(线程不安全)

  4. 懒汉式(线程安全,同步方法)

  5. 懒汉式(线程安全,同步代码块)

  6. 双重检查

  7. 静态内部类

  8. 枚举

1. 饿汉式(静态常量) 推荐

步骤如下:
1) 构造器私有化 (防止 new )
2) 类的内部创建对象
3) 向外暴露一个静态的公共方法。 getInstance
4) 代码实现
//饿汉式(静态变量)
class Singleton {
	
	//1. 构造器私有化, 外部能new
	private Singleton() {
	}
	
	//2.本类内部创建对象实例
	private final static Singleton instance = new Singleton();
	
	//3. 提供一个公有的静态方法,返回实例对象
	public static Singleton getInstance() {
		return instance;
	}
}
优缺点说明:
1) 优点:这种写法比较简单,就是在类装载的时候就完成实例化。避免了线程同
步问题。
2) 缺点:在类装载的时候就完成实例化,没有达到Lazy Loading的效果。如果从始
至终从未使用过这个实例,则会造成内存的浪费
3) 这种方式基于classloder机制避免了多线程的同步问题,不过, instance在类装载
时就实例化,在单例模式中大多数都是调用getInstance方法, 但是导致类装载
的原因有很多种, 因此不能确定有其他的方式(或者其他的静态方法)导致类
装载,这时候初始化instance就没有达到lazy loading的效果
4) 结论:这种单例模式可用, 可能造成内存浪费

2. 饿汉式(静态代码块)

class Singleton {
	
	//1. 构造器私有化, 外部能new
	private Singleton() {
	}
	
	//2.本类内部创建对象实例
	private  static Singleton instance;
	
	static { // 在静态代码块中,创建单例对象
		instance = new Singleton();
	}
	
	//3. 提供一个公有的静态方法,返回实例对象
	public static Singleton getInstance() {
		return instance;
	}
	
}

3. 懒汉式(线程不安全)

class Singleton {
	private static Singleton instance;
	
	private Singleton() {}
	
	//提供一个静态的公有方法,当使用到该方法时,才去创建 instance
	//即懒汉式
	public static Singleton getInstance() {
		if(instance == null) {
			instance = new Singleton();
		}
		return instance;
	}
}
优缺点说明:
1) 起到了Lazy Loading的效果,但是只能在单线程下使用。
2) 如果在多线程下,一个线程进入了if (singleton == null)判断语句块,还未来得及
往下执行,另一个线程也通过了这个判断语句,这时便会产生多个实例。所以
在多线程环境下不可使用这种方式
3) 结论:在实际开发中,不要使用这种方式.

4. 懒汉式(线程安全,同步方法)

	//提供一个静态的公有方法,加入同步处理的代码,解决线程安全问题
	//即懒汉式
	public static synchronized Singleton getInstance() {
		if(instance == null) {
			instance = new Singleton();
		}
		return instance;
	}
优缺点说明:
1) 解决了线程不安全问题
2) 效率太低了,每个线程在想获得类的实例时候,执行getInstance()方法都要进行
同步。而其实这个方法只执行一次实例化代码就够了,后面的想获得该类实例,
直接return就行了。方法进行同步效率太低
3) 结论: 在实际开发中, 不推荐使用这种方式

5. (无用)懒汉式(线程安全,同步代码块)

  • 线程安全都 解决不了
	public static synchronized Singleton getInstance() {
        
		if(instance == null) {
            //两个线程 已经过来了,无意义
            synchronized(Singleton.class){
                instance = new Singleton();
            }
		}
		return instance;
	}
优缺点说明:
1) 这种方式,本意是想对第四种实现方式的改进,因为前面同步方法效率太低,
改为同步产生实例化的的代码块
2) 但是这种同步并不能起到线程同步的作用。跟第3种实现方式遇到的情形一
致,假如一个线程进入了if (singleton == null)判断语句块,还未来得及往下执行,
另一个线程也通过了这个判断语句,这时便会产生多个实例
3) 结论:在实际开发中, 不能使用这种方式

6. 双重检查 推荐

class Singleton {
	private static volatile Singleton instance;
	
	private Singleton() {}
	
	//提供一个静态的公有方法,加入双重检查代码,解决线程安全问题, 同时解决懒加载问题
	//同时保证了效率, 推荐使用
	
	public static synchronized Singleton getInstance() {
		if(instance == null) {
			synchronized (Singleton.class) {
				if(instance == null) {
					instance = new Singleton();
				}
			}
			
		}
		return instance;
	}
}
优缺点说明:
1) Double-Check概念是多线程开发中常使用到的, 如代码中所示,我们进行了两
次if (singleton == null)检查,这样就可以保证线程安全了。
2) 这样,实例化代码只用执行一次,后面再次访问时,判断if (singleton == null),
直接return实例化对象,也避免的反复进行方法同步.
3) 线程安全;延迟加载;效率较高
4) 结论:在实际开发中,推荐使用这种单例设计模式

7. 静态内部类 推荐

  • 主类 装载的时候,静态内部类,不会装载。懒加载

  • 线程安全

    // 静态内部类完成, 推荐使用
    class Singleton {
    
    	//构造器私有化
    	private Singleton() {}
    	
    	//写一个静态内部类,该类中有一个静态属性 Singleton
    	private static class SingletonInstance {
    		private static final Singleton INSTANCE = new Singleton(); 
    	}
    	
    	//提供一个静态的公有方法,直接返回SingletonInstance.INSTANCE
    	
    	public static Singleton getInstance() {
    		
    		return SingletonInstance.INSTANCE;
    	}
    }
    
优缺点说明:
1) 这种方式采用了类装载的机制来保证初始化实例时只有一个线程。
2) 静态内部类方式在Singleton类被装载时并不会立即实例化,而是在需要实例化
时,调用getInstance方法,才会装载SingletonInstance类,从而完成Singleton的
实例化。
3) 类的静态属性只会在第一次加载类的时候初始化,所以在这里, JVM帮助我们
保证了线程的安全性,在类进行初始化时,别的线程是无法进入的。
4) 优点:避免了线程不安全,利用静态内部类特点实现延迟加载,效率高
5) 结论:推荐使用.

8. 枚举 推荐

public class SingletonTest08 {
	public static void main(String[] args) {
		Singleton instance = Singleton.INSTANCE;
		Singleton instance2 = Singleton.INSTANCE;
        
		System.out.println(instance == instance2);
		
		System.out.println(instance.hashCode());
		System.out.println(instance2.hashCode());
		
		instance.sayOK();
	}
}

//使用枚举,可以实现单例, 推荐
enum Singleton {
	INSTANCE; //属性
	public void sayOK() {
		System.out.println("ok~");
	}
}
优缺点说明:
1) 这借助JDK1.5中添加的枚举来实现单例模式。不仅能避免多线程同步问题,而
且还能防止反序列化重新创建新的对象。
2) 这种方式是Effective Java作者Josh Bloch 提倡的方式
3) 结论:推荐使用

其他的方法:

1. 全局静态变量(不算)

public class Singleton_00 {

    //jvm初始化的过程中,实例化属性。保存全局的对象
    public static Map<String, String> cache = new ConcurrentHashMap<String, String>();
}

2. CAS

public class Singleton_06 {
	//定义 atomic
    private static final AtomicReference<Singleton_06> INSTANCE = new AtomicReference<Singleton_06>();

    //private static Singleton_06 instance;//这个变量没用,最好注释

    private Singleton_06() {
    }

    //final 可以省略
    public static final Singleton_06 getInstance() {
        //死循环
        for (; ; ) {
            //获得 这个对象。用静态的也行吧
            Singleton_06 instance = INSTANCE.get();
            //如果不为null,就返回
            if (null != instance) return instance;
            
            //为null,就创建
            //无锁 算法,CPU指令集操作,只有一步 原子操作
            INSTANCE.compareAndSet(null, new Singleton_06());
            //然后 返回
            return INSTANCE.get();
        }
    }

}
Reference 

n. 提及,谈到;参考,查阅;(引自书或诗歌的)引言,引文;引文的作者,参考书目;(帮助或意见的)征求,征询; (为方便查询所用的)标记,编号;推荐信,介绍信;介绍人,推荐人

adj. 参考的,用于查阅的;文献索引的,参照的

v. 列出……的参考书目;提及,提到;引用,参照(某书或某作者)

JDK 使用单例

我们JDK中, java.lang.Runtime就是经典的单例模式(饿汉式)

public class Runtime {
	private static Runtime currentRuntime = new Runtime();

    public static Runtime getRuntime() {
        return currentRuntime;
    }


    private Runtime() {}
    
}

单例模式注意事项和细节说明

  1. 单例模式保证了 系统内存中该类只存在一个对象,节省了系统资源,对于一些需
    要频繁创建销毁的对象,使用单例模式可以提高系统性能

  2. 当想实例化一个单例类的时候,必须要记住使用相应的获取对象的方法,而不是使
    用new

  3. 单例模式使用的场景:需要频繁的进行创建和销毁的对象、创建对象时耗时过多或
    耗费资源过多(即:重量级对象), 但又经常用到的对象、工具类对象、频繁访问数
    据库或文件的对象(比如数据源、 session工厂等)

Runtime

打印内存 和 垃圾回收

Runtime类描述了虚拟机一些信息。
该类采用了单例设计模式,
可以通过静态方法 getRuntime()获取Runtime类实例。

Runtime runtime = Runtime.getRuntime();    
    
System.out.println("total memory:"+runtime.totalMemory()/(1024*1024) + "M");

System.out.println("max memory:" + runtime.maxMemory()/(1024*1024) + "M");

System.out.println("free memory:" + runtime.freeMemory()/(1024*1024) + "M");

Runtime类提供gc()方法,用于释放Java虚拟机的一些无用空间。
gc是garbage collection的缩写,就是垃圾回收的意思。

runtime.gc();

调用系统的程序

Runtime类可以调用本机的一些可执行程序,并且创建进程。exec(String cmd)方法可以打开一个可执行程序,下面代码演示了打开windows记事本程序并5秒后关闭。


Process process = runtime.exec("notepad");

Thread.sleep(1000*5);

process.destroy();

1. UML

1) UML——Unified modeling language UML
(统一建模语言),是一种用于软件系统
分析和设计的语言工具,它用于帮助软
件开发人员进行思考和记录思路的结果

2) UML本身是一套符号的规定,就像数学
符号和化学符号一样,这些符号用于描
述软件模型中的各个元素和他们之间的
关系,比如类、接口、

依赖、----->		虚线
关联、_______		实线
继承(泛化)、————▷	实线 ___▷

实现、------▷	虚线
聚合、——————◇	实线 ____◇
组合、——————♦	实线 ____♦

3) 使用UML来建模,常用的工具有 Rational
Rose , 也可以使用一些插件来建模
rational 
英 /ˈræʃnəl/  美 /ˈræʃ(ə)nəl/  全球(英国)  
adj. (想法、决定等)合理的,基于理性的;(人)理性的,理智的;富有理性的;(数)有理的,有理数的

n. 有理数
为了画UML图,在Eclipse安装AmaterasUML
可以画:
activity diagram 活动图
class diagram	类图
sequence diagram 时序图
usecase diagram	用例图

图形表示

  • 依赖(使用)

    • Dependency
    • 方法的参数 (a:A)
    • 依赖,箭头,剑身是虚线
    • ------------>
    • 构造传递也是 方法,也是依赖。
  • 关联

    • association
    • 一条线,一对一,一对n
    • ————
  • 泛化(继承)

    • Generalization
    • 泛化,就是 空心的三角箭头,实心的线
    • ————▷
  • 实现

    • implementation 和 realization
    • 空心的三角形,虚线
    • --------------▷
  • 聚合

    • Aggregation
    • 定义属性 a:A,
      • 使用 setA(a:A) 方法赋值
      • 当前类X,聚合了 A类
    • 依赖方为 菱形(空心),实线方 为 被依赖,如上例的A
    • ————◇
    • 可分离
  • 组合

    • 组合 B类里,new A();
      • 耦合性 比聚合强
    • composite
    • 依赖方为 菱形(实心)
    • ———♦
    • 不可分离

所属单词

1. Dependency

2. association 
简明 牛津 新牛津  韦氏  柯林斯 例句 百科
n. 协会,社团,联盟;交往,联合;联系,因果关系;联想
复数 associations

3. Gen era li zation
Generalization 
英 /ˌdʒenrəlaɪˈzeɪʃn/  美 /ˌdʒenrələˈzeɪʃn/  全球(美国)  
简明 牛津 新牛津  韦氏  柯林斯 例句 百科
n. (依据不足的)概括,泛论(=generalisation)

4. implementation 和 realization
realization 
英 /ˌriːəlaɪˈzeɪʃn; ˌrɪəlaɪˈzeɪʃn/  美 /ˌriːələˈzeɪʃn/  全球(英国)  
简明 牛津 新牛津  韦氏  例句 百科
n. 认识,领悟;(目标等的)实现;变现,变卖;兑谱,改编;(语言特征的)体现;货物销售;发声,(戏剧)演出,设计成果

5. Aggregation 
英 /ˌæɡrɪˈɡeɪʃn/  美 /ˌæɡrɪˈɡeɪʃn/  全球(美国)  
简明 韦氏  柯林斯 例句 百科
n. 聚集,聚合;(互联网)相关内容项的集合;聚集体,集合体

6. composition
简明 牛津 新牛津  韦氏  柯林斯 例句 百科
n. 成分构成,成分;(音乐、艺术、诗歌的)作品;创作,作曲;构图;作文
复数 compositions
composite 
简明 牛津 新牛津  韦氏  柯林斯 例句 百科
adj. 合成的,复合的;(火车车厢)综合的;菊科的;(柱式)混合的
n. 合成物,复合材料;罪犯画像;综合提案;菊科植物;混合柱式
v. 合成(图片)
terminate 
简明 牛津 新牛津  韦氏  柯林斯 例句 百科
v. (使)结束,(使)终止;到达终点站;终止妊娠,人工流产;<美>解雇;<美>谋杀(某人);在……结尾,以……收尾

relation
英 /rɪˈleɪʃn/ 美 /rɪˈleɪʃn/
n. (人或物间的)联系;(国家、公司、组织等之间的)(正式)关系;<正式> 性关系;亲戚,亲属;讲述,叙述

dispatch
英 /dɪˈspætʃ/ 美 /dɪˈspætʃ/
v. 派遣;发送;迅速处理,迅速办妥;杀死,处决
n. 派遣,发送;公文,急件;报道,电讯;杀死,除掉

UML图 划分

1) 用例图(use case)
2) 静态结构图: 类图、对象图、包图、组件图、部署图
3) 动态行为图:交互图(时序图与协作图)、状态图、活动图

说明:
1) 类图是描述类与类之间的关系的,是UML图中最核心的

极简案例

public class Person{ //代码形式->类图
	private Integer id;
	private String name;
	public void setName(String name){
		this.name=name;
	}
	public String getName(){
		return  name;
	}
}
com.xxx.Person

id: Integer
name: String

setName(name:String): void
getName(): String

依赖 ---->

  • A 用到 B,称: A 依赖于 B
  • 箭头的方向指向B

类图—依赖关系(Dependence)
只要是在类中用到了对方, 那么他们之间就存在依赖关系。如果没有对方,连编
绎都通过不了。

全局/局部变量,形参,返回值

public class PersonServiceBean {
    //全局变量。用 聚合也没错
    private PersonDao personDao;//类

    //参数
    public void save(Person person) {
    }

    //返回值
    public IDCard getIDCard(Integer personid) {
    }

    public void modify() {
        //违反了 迪米特。如果这叫依赖,那 依赖包含:组合
        Department department = new Department();
    }
}

public class PersonDao {
}

public class IDCard {
}

public class Person {
}

public class Department {
    //同上 是组合关系
}
  • 去掉+号
PersonServiceBean

+ personDao: PersonDao	//用 聚合也没错。聚合的话,菱形在 主类

+ save(person:Person): void
+ getIDCard(personid:Integer): IDCard
+ modify(): void
小结
类中用到了对方

1) 如果是类的成员属性
2) 如果是方法的返回类型
3) 是方法接收的参数类型
4) 方法中使用到(局部变量)

泛化(继承)—▷

类图—泛化关系(generalization)
泛化关系实际上就是继承关系,他是依赖关系的特例
        
public abstract class DaoSupport{
	public void save(Object entity){
	}
	public void delete(Object id){
	}
}//箭头的头指向

//箭头的尾巴
public class PersonServiceBean extends Daosupport{
}
        
小结:
1) 泛化关系实际上就是继承关系
2) 如果A类继承了B类,我们就说AB存在泛化关系

实现----▷

类图—实现关系(Implementation)
实现关系实际上就是A类实现B接口, 他是依赖关系的特例

public interface PersonService {
	public void delete(Interger id);
}//箭头

//箭 尾巴
public class PersonServiceBean implements PersonService {
	public void delete(Interger id){}
}

关联 —

类图—关联关系(Association)
关联关系实际上就是类与类之间的联系,他是依赖关系的特例

关联具有导航性:即双向关系或单向关系
关系具有多重性:

  • 如“1”(表示有且仅有一个),
  • “0…”(表示0个或者多个),
  • “0, 1”(表示0个或者一个),
  • “n…m”(表示n到 m个都可以),
  • “m…*”(表示至少m
    个)。
单向一对一关系
public class Person {
    // Person —> IDCard
	private IDCard card;
}
public class IDCard{}

双向一对一关系	1————1
public class Person {
	private IDCard card;
}
public class IDCard{
	private Person person
}  
聚合—◇

类图—聚合关系(Aggregation)
聚合关系(Aggregation)表示的是

  • 整体和部分的关系, 整体与部分可以分开。

  • 聚合关系是 关联关系的特例,所以他具有关联的导航性与多重性。

  • 如:一台电脑由键盘(keyboard)、显示器(monitor),鼠标等组成;组成电脑的各个

    • 配件是可以从电脑上分离出来的, 使用带空心菱形的实线来表示:
    • 不可以分开,是组合的关系。
//空心菱形,指向整体的 部分
public class Computer {
	private Mouse mouse; //鼠标可以和computer分离
	private Moniter moniter;//显示器可以和Computer分离
	public void setMouse(Mouse mouse) {
		this.mouse = mouse;
	}
	public void setMoniter(Moniter moniter) {
		this.moniter = moniter;
	}
	
}
组合—♦

也是关联的特例

如果我们人Mouse,Monitor和Computer是不可分离的,则升级为组合关系

  • 共生共灭
//主类,对着 黑心的菱形
public class Computer {
	private Mouse mouse = new Mouse(); //鼠标可以和computer不能分离
	private Moniter moniter = new Moniter();//显示器可以和Computer不能分离
	public void setMouse(Mouse mouse) {
		this.mouse = mouse;
	}
	public void setMoniter(Moniter moniter) {
		this.moniter = moniter;
	}
}

Composition
组合关系:也是整体与部分的关系,但是整体与部分不可以分开。
再看一个案例:在程序中我们定义实体: Person与IDCard、 Head,

  • 那么 Head 和 Person 就是 组合,
  • IDCard 和 Person 就是聚合。
    • 但是如果在程序中Person实体中定义了对IDCard进行级联删除
      • 删除所有的人,班级也删除。就是:组合关系。
    • 即删除Person时连同IDCard一起删除, 那么IDCard 和 Person 就是组合了.
public class Person{
private IDCard card;

//头,不能分开
private Head head = new Head();
}
public class IDCard{}
public class Head{}  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值