Fork me on GitHub

Java设计模式之单例模式

  设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。 毫无疑问,设计模式于己于他人于系统都是多赢的;设计模式使代码编制真正工程化;设计模式是软件工程的基石脉络,如同大厦的结构一样(百度百科)。

设计模式分类:

创建型模式:

  单例模式、工厂模式、抽象工厂模式、建造者模式、原型模式

结构型模式:

  适配器模式、桥接模式、装饰模式、组合模式、外观模式、代理模式

行为模式:

  责任链模式、命令模式、策略模式、观察模式、解释器模式、迭代模式、中介模式、备忘录模式、状态模式、模板模式、访问者模式

设计原则

  开闭原则: 一个软件实体应该对扩展开放 对修改闭合。

  里氏代换原则:  任何能使用父类的地方一定能使用子类。

  依赖倒转原则: 要依赖于抽象 不要依赖于实现。或者是抽象不应该依赖与细节,细节应该依赖于抽象。

  合成聚合复用原则: 尽量使用合成聚合而不是继承去实现复用。

  迪米特法则: 一个软件实体应该尽可能少的与其它实体发生相互作用。

  接口隔离原则: 应当为客户提供尽可能小的单独的接口 而不应该提供大的综合性的接口。

  今天自己学习了一下单例模式。

单例模式的核心作用

  保证一个类只有一个实例,并且提供一个访问该实例的全局访问点。意味着只有一个存在的对象。

单例的优点:

  只生成一个实例,减少系统开销,提高系统性能。

  提供一个全局的访问点,资源共享。

单例的优点:

  扩展性比较差。

  滥用单例将带来一些负面问题,如为了节省资源将数据库连接池对象设计为的单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出;如果实例化的对象长时间不被利用,系统会认为是垃圾而被回收,这将导致对象状态的丢失。

 

常见运用场景:

  代码中经常使用的读取配置文件的类,一般我们只会有一个对象。没有必要每次读取一个对象都创建新对象。

  Windows的任务管理器,怎么按都会只有一个出来。

  日志对象,我们一般也会使用单例。

常见的5种单例模式实现方式:

  主要:

    饿汉式(线程安全、调用效率高。不能延时加载 以空间换时间)。

    懒汉式(线程安全、调用效率不高,可以延时加载 以时间换空间)。

  其他:

    双重检测锁式(JVM内存的“无序写入”问题 不建议使用)

    静态内部类式(线程安全、调用效率高,延时加载)

    枚举单例(线程安全、调用效率高,不能延时加载)

  懒汉式单例:

package com.roc.singlet;

/**

 * 懒汉式单例

 * @author liaowp

 *

 */

public class SingletPatter1 {

//类初始化时,立即加载这个对象。加载类时,线程安全。

private static SingletPatter1 instance = new SingletPatter1();

 

private SingletPatter1(){

 

}

 

public static SingletPatter1 getInstance(){

return instance;

}

}

懒汉式:

package com.roc.singlet;

/**

 * 懒汉式(线程安全、调用效率不高,可以延时加载 以时间换空间,对象延迟加载)。

 * @author liaowp

 *

 */

public class SingletPatter2 {

//初始化时没有立即创建对象。

private static SingletPatter2 instance;

 

//私有话构造器

private SingletPatter2(){

 

}

 

public static synchronized SingletPatter2 getInstance(){

if(instance==null){

instance = new SingletPatter2();

}

return instance;

}

}

主要特点:懒加载(延时加载)使用的时候才加载。

存在问题:每次都要同步,并发效率低。

怎么理解2种类型的单例:

  饿汉:想一想你饿的时候马上就想去吃东西,立即就加载,这就是饿汉。

  懒汉:即时自己很饿了,但是很懒。我想要吃的时才去吃。使用的时候才加载。这是懒汉。

双重检测锁实现:

 1 package com.roc.singlet;
 2 
 3 /**
 4 
 5  * 双重检测锁实现
 6 
 7  * @author liaowp
 8 
 9  *
10 
11  */
12 
13 public class SingletPatter3 {
14 
15 public static SingletPatter3 instance = null;
16 
17  
18 
19 //每个模式将同步内容下发到if内部,提高执行效率。只有第一次才同步,创建以后就没有必要了
20 
21 public static SingletPatter3 getInstance(){
22 
23 if(instance==null){
24 
25 SingletPatter3 singletPatter3;
26 
27 synchronized (SingletPatter3.class) {
28 
29 singletPatter3 = instance;
30 
31 if(singletPatter3==null){
32 
33 synchronized (SingletPatter3.class) {
34 
35 if(singletPatter3==null){
36 
37 singletPatter3 = new SingletPatter3();
38 
39 }
40 
41 }
42 
43 instance = singletPatter3;
44 
45 }
46 
47 }
48 
49 }
50 
51 return instance;
52 
53 }
54 
55  
56 
57 private SingletPatter3(){
58 
59  
60 
61 }
62 
63 }

存在问题:由于编译器优化原因和JVM底层内部模型原因。偶尔会出现问题。

静态内部类的实现方式:

package com.roc.singlet;

/**

 * 静态内部类实现方式

 * @author liaowp

 *

 */

public class SingletPatter4 {

 

private static class SingletPatterInstance{

private static SingletPatter4 instance = new SingletPatter4();

}

 

public static SingletPatter4 getInstance(){//使用到才去调用内部类加载方式,即懒加载

return SingletPatterInstance.instance;

}

 

public SingletPatter4(){

 

}

}

优点:

外部没有static修饰,不会立即加载。

Instance的是final static的类型,保证了内存中只有一个实例。线程安全。

兼备并发高效的调用和延迟加载的优势。

枚举实现方式

package com.roc.singlet;

/**

 * 枚举方式实现单例

 * @author liaowp

 *

 */

public enum SingletPatter5 {  

    INSTANCE;  

}  

如何选择:

单例对象,占用资源少,不需要延时加载的时候可以选择枚举式与饿汉式。

枚举式  》   饿汉式。

单例对象 占用资源大 需要延时加载:静态内部类式 懒汉式

静态内部类式 》 懒汉式

posted @ 2016-03-30 23:20  鹏&鹏  阅读(697)  评论(0编辑  收藏  举报