单例模式:用来创建独一无二的,只能有一个实例的对象的入场券。
一些对象,我们只需要一个:(线程池,缓存,对话框等等),事实上,这类对象只能有一个实例。如果制造多了了,会导致许多问题,如行为异常、资源使用过量。
全局变量的缺点,如果将对象赋值给一个全局变量,那么必须在程序一开始就创建好对象,万一这个对象非常消耗资源,而程序在这次的执行过程中又一直没用到它,就形成了浪费。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
public
class
Singleton {
private
static
Singleton uniInstance;
public
int
ID;
private
Singleton(
int
id) {
ID = id;
}
public
static
Singleton getInstance(
int
id) {
if
(uniInstance ==
null
) {
uniInstance =
new
Singleton(id);
}
return
uniInstance;
}
}
//----------------------Main------------------------
public
class
Main {
public
static
void
main(String[] args) {
Singleton mSingleton,nSingleton;
mSingleton = Singleton.getInstance(
3
);
nSingleton = Singleton.getInstance(
4
);
System.out.println(mSingleton.ID);
System.err.println(nSingleton.ID);
}
}
|
打印出来的结果,都是3,说明mSingleton与nSingleton指向同一对象,保证了实例的唯一性。
请注意,如果不需要这个实例,它就永远不会产生。
上述为单例模式的经典实现,再看下单例模式的定义:
确保一个类只有一个实例,并提供一个全局访问点。
把类设计成自己管理的一个单独实例,同时也避免其他类再自行产生实例。并且提供了这个实例的全局访问点:当你需要实例时,向类查询,它会返回单个实例。
需要注意的是,Singleton声明的实例对象必须是全局的。假设上述代码中的
1
|
Singleton mSingleton,nSingleton;
|
是分别放在不同线程中声明的,那么getInstance获得的实例就不符合单件模式规则了。
巧克力工厂:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
|
/**
* 这是一个巧克力锅炉控制器,锅炉做的事,就是把巧克力和牛奶融在一起, 然后送到下一个阶段,以制造成巧克力棒
*/
public
class
ChocolateBoiler {
private
boolean
empty;
// 代码开始时,锅炉是空的
private
boolean
boiled;
public
ChocolateBoiler() {
empty =
true
;
boiled =
false
;
}
/**
* 在锅炉内填入原料时,锅炉必须是空的。 一旦填入原料,就把empty和boiled标志设置好
*/
public
void
fill() {
if
(isEmpty()) {
empty =
false
;
boiled =
true
;
// 在锅炉内填满巧克力和牛奶的混合物
}
}
private
boolean
isEmpty() {
return
empty;
}
private
boolean
isBoiled() {
return
boiled;
}
/**
* 锅炉排出时,必须是满的(不可以是空的)而且是煮过的。排出完毕后,把empty标志设回true
*/
public
void
drain() {
if
(!isEmpty() && isBoiled()) {
// 排除煮沸的巧克力和牛奶
empty =
true
;
}
}
/**
* 煮混合物时,锅炉必须是满的,并且是没有煮过的,一旦煮沸后,就把boiled标志设为true
*/
public
void
boil() {
if
(!isEmpty() && !isBoiled()) {
// 将炉内物煮沸
boiled =
true
;
}
}
}
|
注意到了,如果同时存在两个以上ChocolateBoiler实例存在,可能会存在的问题:排除500升的为主费的混合物,或者锅炉已经满了还继续放原料,或者锅炉内还没放原料就开始空烧。修改下代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
public
class
ChocolateBoiler {
private
boolean
empty;
// 代码开始时,锅炉是空的
private
boolean
boiled;
private
static
ChocolateBoiler uniChocolateBoiler;
private
ChocolateBoiler() {
// TODO Auto-generated constructor stub
empty =
true
;
boiled =
false
;
}
public
static
ChocolateBoiler getInstance() {
if
(uniChocolateBoiler ==
null
) {
uniChocolateBoiler =
new
ChocolateBoiler();
}
return
uniChocolateBoiler;
}
//后面代码省略
}
|
又遇到了麻烦,fill方法允许在加热的过程中继续假如原料,这可是会溢出五百升的原料。是多线程导致的问题。
处理多线程,只要把getInstance()变成同步(synchronized)方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
class
Singleton {
private
static
Singleton uniqueInstance;
private
Singleton() {
}
public
static
synchronized
Singleton getInstance() {
if
(uniqueInstance ==
null
) {
uniqueInstance =
new
Singleton();
}
return
uniqueInstance;
}
}
|
综述
1.单件模式确保程序中一个类最多只有一个实例
2.单件模式也提供访问这个实例的全局点
3.在java中实现单件模式需要
(1)私有构造器
(2)一个静态方法
(3)一个静态变量
4.确定在性能和资源上的限制,然后小心地选择适当的方案来实现单件,以解决多线程的问题
由于最近一直在学习Golang,所以之后学习的设计模式如果没有涉及到Java特有的语言特性,学习笔记中的源码将由Golang来完成