1、单例模式:
确保一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。
单例模式有以下几个要素:
- 私有的构造方法
- 指向自己实例的私有静态引用
- 以自己实例为返回值的静态的公有的方法
单例模式根据实例化对象时机的不同分为两种:一种是饿汉式单例,一种是懒汉式单例。饿汉式单例在单例类被加载时候,就实例化一个对象交给自己的引用;而懒汉式在调用取得实例方法的时候才会实例化对象。
饿汉式:
1
2
3
4
5
6
7
8
9
10
11
|
public class Singleton_Simple
{ private static final Singleton_Simple
simple = new Singleton_Simple(); private Singleton_Simple(){} public static Singleton_Simple
getInstance(){ return simple; } } |
懒汉式:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
//双锁机制 class Singleton
{ private volatile static Singleton
singleton; public static Singleton
getInstance() { if (singleton
== null )
{ synchronized (Singleton. class )
{ //
由于每次调用都需要进行同步,所以可以在前面进行判断即可提高效率,同时注意使用的是Singleton.class的类锁 if (singleton
== null )
{ singleton
= new Singleton(); } } } return singleton; } } |
为了提高效率。我们可以用double check机制,现在来看两个问题:
(1)为何用double check的检测机制?
由于两个线程都可以通过第一重的判断 ,进入后,由于存在锁机制,所以会有一个线程进入同步块和第二重判断,而另外的一个线程在同步块处阻塞。
当第一个线程退出同步代码块后,第二个进入,没有经过第二重判断,保证了单例。所以这里必须要使用双重检查锁定。
其实没有第一重的判断,我们也可以实现单例,只是为了考虑性能问题。
(2)为何要对实例变量加volatile关键字?
1.为对象分配内存
2.初始化实例对象
3.把引用instance指向分配的内存空间
ps:对象创建可参看《深入理解Java虚拟机》第44页
这个三个步骤并不能保证按序执行,处理器会进行指令重排序优化,存在这样的情况:
优化重排后执行顺序为:1,3,2, 这样在线程1执行到3时,instance已经不为null了,线程2此时判断instance!=null,则直接返回instance引用,但现在实例对象还没有初始化完毕,此时线程2使用instance可能会造成程序崩溃。
现在要解决的问题就是怎样限制处理器进行指令优化重排。
volatile的作用:
(1)volatile变量不会以被缓存到寄存器或者对其它处理器不可见的地方,因此在读取volatile类型的变量时总是返回最新写入的值。
(2)volatile关键字能够通过提供内存屏障,来保证某些指令顺序处理器不能够优化重排,编译器在生成字节码时,会在指令序列中插入内存屏障来禁止特定类型的处理器重排序。
JVM会对分配内存空间的动作进行同步处理。
(1)可能实际上采用CAS(Compare And Set)配上失败重试的方式保证更新操作的原子性
(2)把内存分配的动作按照线程划分在不同的空间之中进行。就是每个线程在Java堆中预先分配一小块内存,
参考Java虚拟机第45页
其实还有种写法,如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
public class Singleton
{ private static class SingletonClassInstance
{ //
私有静态内部类 //
可能是final解决了并发的问题,基本类型声明就可,但是对象类型时,这个对象不能有状态, //
如果有状态,则一定要声明为final,例如String类就是一个不可变类 private static final Singleton
instance = new Singleton(); } public static Singleton
getInstance() { return SingletonClassInstance.instance; //
只在这里调用了静态内部类,私有属性让他人无法使用这个类型 } private Singleton()
{ } } |
由于Singletom没有static属性且SingletonClassInstance是私有静态内部类,不会被其他类调用,所以不会被提前初始化,实现了懒汉式的构造实例。
重点要知道为什么静态内部类能保证单例:因为只有一个线程去初始化这个类,在初始化后static final就是一个常量了,当然线程安全了。
其在实际中有重要的应用,如:
2、迭代器模式:
提供一种方法访问一个容器对象中各个元素,而又不暴露该对象的内部细节
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
|
interface Iterable{ public Iterator
iterator(); } interface Aggregate extends Iterable{ public void add(Object
obj); public void remove(Object
obj); public Iterator
iterator(); } class ConcreteAggregate implements Aggregate
{ private List
list = new ArrayList(); public void add(Object
obj) { list.add(obj); } public Iterator
iterator() { return new ConcreteIterator(list); } public void remove(Object
obj) { list.remove(obj); } } |
调用iterator()方法后返回一个Iterator迭代器,实现如下:
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
|
interface Iterator
{ public Object
next(); public boolean hasNext(); } class ConcreteIterator implements Iterator
{ private List
list = new ArrayList(); private int cursor
= 0 ; public ConcreteIterator(List
list) { this .list
= list; } public boolean hasNext()
{ if (cursor
== list.size()) { return false ; } return true ; } public Object
next() { Object
obj = null ; if ( this .hasNext())
{ obj
= this .list.get(cursor++); } return obj; } } |
测试一下:
1
2
3
4
5
6
7
8
9
|
Aggregate
ag = new ConcreteAggregate(); ag.add( "小明" ); ag.add( "小红" ); ag.add( "小刚" ); Iterator
it = ag.iterator(); while (it.hasNext())
{ String
str = (String) it.next(); System.out.println(str); } |
3、组合设计模式:
蜜蜂是昆虫:继承实现
蜜蜂有向前移动后攻击人的行为:组合实现
昆虫:
1
2
3
4
5
6
7
8
9
10
11
12
|
class Insect
{ private String
name; public Insect(String
name) { this .name
= name; } public String
getName() { return name; } public void setName(String
name) { this .name
= name; } } |
攻击行为:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
interface Attack
{ public void move(); public void attack(); } class AttackImpl implements Attack
{ private String
move; private String
attack; public Attack
Impl(String move, String attack) { this .move
= move; this .attack
= attack; } @Override public void move()
{ System.out.println(move); } @Override public void attack()
{ move(); System.out.println(attack); } } |
蜜蜂是昆虫,有向前一步攻击人的属性:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
class Bee extends Insect implements Attack
{ private Attack
attack; public Bee(String
name, Attack attack) { super (name); this .attack
= attack; } public void move()
{ attack.move(); } public void attack()
{ attack.attack(); } } |
由于继承机制太过依赖于父类的实现细节,如果父类变动,则子类也会跟着变动,这是糟糕的。
4、策略设计模式:
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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
|
//
Different types of function objects: interface Combiner<T>
{ T
combine(T x, T y); } public class Functional
{ //
Calls the Combiner object on each element to combine //
it with a running result, which is finally returned: public static <T>
T reduce(Iterable<T> seq, Combiner<T> combiner) { Iterator<T>
it = seq.iterator(); if (it.hasNext())
{ T
result = it.next(); while (it.hasNext()){ result
= combiner.combine(result, it.next()); } return result; } //
If seq is the empty list: return null ; //
Or throw exception } //
To use the above generic methods, we need to create //
function objects to adapt to our particular needs: static class IntegerAdder implements Combiner<Integer>
{ public Integer
combine(Integer x, Integer y) { return x
+ y; } } static class IntegerSubtracter implements Combiner<Integer>
{ public Integer
combine(Integer x, Integer y) { return x
- y; } } static class BigIntegerAdder implements Combiner<BigInteger>
{ public BigInteger
combine(BigInteger x, BigInteger y) { return x.add(y); } } public static void main(String[]
args) { //
Generics, varargs & boxing working together: List<Integer>
li = Arrays.asList( 1 , 2 , 3 , 4 , 5 , 6 , 7 ); Integer
result = reduce(li, new IntegerAdder()); print(result); //
28 result
= reduce(li, new IntegerSubtracter()); print(result); //
Use the prime-generation facility of BigInteger: List<BigInteger>
lbi = new ArrayList<BigInteger>(); BigInteger
bi = BigInteger.valueOf( 11 ); for ( int i
= 0 ;
i < 11 ;
i++) { lbi.add(bi); bi
= bi.nextProbablePrime(); } print(lbi); BigInteger
rbi = reduce(lbi, new BigIntegerAdder()); print(rbi); } } |
如上例子参考了Java编程思想关于泛型实现策略模式的例子。
5、原型设计模式
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
|
class bottle implements Cloneable
{ public Wine
wine; public bottle(Wine
wn) { this .wine
= wn; } //
覆写clone()方法 protected Object
clone() throws CloneNotSupportedException
{ bottle
newBottle = (bottle) super .clone(); newBottle.wine
= (Wine) wine.clone(); return newBottle; } } class Wine implements Cloneable
{ int degree; String
name= "法国白兰地" ; //
字符串虽然是引用类型,但是新对象中修改时并不会影响到原对象值 public int getDegree()
{ return degree; } public void setDegree( int degree)
{ this .degree
= degree; } //
覆写clone()方法 protected Object
clone() throws CloneNotSupportedException
{ return super .clone(); } } |
下面来总结一下各个设计模式在Java中的具体应用。
结构型模式 | |
单例模式(Single Pattern) |
1、Log4j中将日志输出到一个文件中必须使用单例模式,否则会覆盖文件的原有内容 2、数据库连接池中的应用 |
工厂模式(Factory) |
Spring工厂模式:通过XML文件或Java注解来表示Bean之间的依赖关系,很少直接new一个类进行代码编写 |
建造者模式(Builder) |
Struts2中创建容器(Container)对象 |
原型模式(Protype) |
Java的克隆clone() |
结构型模式 | |
适配器模式(Adapter) |
1、在Java的I/O类库中,StringReader将一个String类适配到Reader接口,InputStreamReader将InputStream适配到Reader类。 2、在Spring中的AOP中,由于Advisor需要的是MethodInterceptor对象,所以每一个Advisor中的Advice都要配成对应的MethodInterceptor对象。 |
桥接模式(Bridge) |
|
装饰模式(Decorator) |
1、Collections.synchronizedList例子也是一个装饰器模式 2、装饰器在Junit中的应用。TestDecorator是Test的装饰类,其TestDecorator有一些扩展功能的子类。如RepeatedTest类,TestSetup类等 3、一般情况下,需要使用FileReader和StringReader,如果要增加缓存功能的类有很多,那么子类也就需要很多,所以Java就使用了装饰模式,BufferedReader就是这样的装饰类。其实Java I/O 库中的所有输入流、输出流的类都采用了装饰器模式,它们可以无限次地进行装饰转换,转换的目的就是得到自己想要的数据类型的流对象
优点:动态扩展类功能属性,而无需通过继承层次结构实现,更方便灵活给类添加职责 |
组合模式(Composite) |
|
外观模式(Façade) |
1、在Spring中已经提供了很好的封装,在org.springframework.jdbc.support包下提供的JdbcUtils类就是这样的工具类 2、在Hibernate的配置工作中,有一个Configuration类,负责管理运行时需要的一些信息 |
享元模式(Flyweight) |
1、数据据连接池是享元模式的重要应用 2、在Java中,对于基本类型和字符串类型的实现就采用了享元模式 |
代理模式(Proxy) |
1、在Spring中已经提供了很好的封装,在org.springframework.jdbc.support包下提供的JdbcUtils类就是这样的工具类 2、在Hibernate的配置工作中,有一个Configuration类,负责管理运行时需要的一些信息 |
行为型模式 | |
模版方法模式(Template Method) |
1、在Junit中的TestCase类中 2、在MVC框架的HttpServlet中 3、Spring采用开放式的处理方式,在使用JpaTemplate时,只需要处理具体的SQL语句即可,而创建连接、关闭连接等方法都不需要编写代码,因为不管理是哪种持久层的操作应运,其创建和关闭连接都是一致的,按照相同的顺序执行,这就是模板方法模式的应用 |
命令模式(Command )
|
命令模式在Struts2中的应用。其中action就是命令模式中命令接口,ActionInvocation就是命令模式中命令的调用者 |
迭代器模式(Iterator ) |
java中的Collection、List、Set、Map等,这些集合都有自己的迭代器。 |
观察者模式(Oberver Pattern) |
|
中介者模式(Mediator) |
|
备忘录模式(Memento) |
|
解释器模式(Interpreter) |
|
状态模式(State) |
|
职责链模式(Chain of Responsibility) |
责任链在Struts2中的拦截器上有重要应用 |
策略模式(Strategy) |
1、策略模式允许在程序执行时选择不同的算法.比如在排序时,传入不同的比较器(Comparator),就采用不同的算法 2、Spring的Resource实现思想是典型的策略模式的应用 |
访问者模式(Visitor) |
1、javac中关于语法树各个节点的语义分析就使用访问者模式来实现的 |