浅谈java设计模式那些事之创建型模式(一)

我们都知道java中一共有23种设计模式,但是大体上可以分为三类模式:

  • 创建型模式
    创建型模式顾名思义就是用来帮助我们创建对象的。创建着模式又可以分为:单例设计模式、工厂模式(简单工厂模式、工厂方法模式和抽象工厂模式)、建造者模式和原型模式。
  • 结构型模式
    结构型模式包括:适配器模式 、桥接模式、 装饰模式 、组合模式 、外观模式 、享元模式 、代理模式
  • 行为型模式
    行为模式包括:模板方法模式、命令模式 、迭代器模式 、 观察者模式 、中介者模式 、备忘录模式、解释器模式 、状态模式 、策略模式 、职责链模式 、访问者模式
    有这么种设计模式,我们不可能全部学会。所以,我们在学习的时候应该有选择的性的去学习。
    今天,我主要想和大家聊一下创建型模式中的单例设计模式。前面已经说过,创建型模式他的作用就是帮助我们来进行创建对象的。好,那么接下来我们就来看23种设计模式中最简单又是最重要的一种,就是单例设计模式:

单例设计模式
那么问题来了,什么是单例设计模式?最简单明了的回答:
就是保证一个类只能有一个实例对象,并且提供一个访问该实例的全局访问点
单例模式的优点:
由于单例模式只生成一个实例,减少了系统的开销,当一个对象的产生需要比较多的资源时,如读取配置文件,产生其他依赖对象时,则可以直接产生一个单例对象,永久的提供服务。
当然,单例设计模式的应用场景也是很多,常见的应用场景如下:

  • windows的任务管理器。
  • windows中的回收站。
  • 项目中,读取配置文件的类,一般也只有一个对象。
  • 网站计数器。
  • 应用程序的日志应用。
  • 数据库连接池。
  • 操作系统的文件系统。
  • Application(Servlet中会涉及到)。
  • 在Spring中,每个bean都是单例模式的。
  • 在Servlet编程中,每个Servlet也都是单例设计模式。

好了,干货来了。说了这么多,大家肯定想,单例设计模式到底用代码怎么实现,精彩即将上演(不过,我要谁告诉大家,单例设计模式的实现有5种方法)。
第一种:饿汉式实现单例设计模式

/**
 *  饿汉式实现单例模式
 * @author tuofafa
 *	特点:私有化构造函数  对外提供一个创建对象的方法
 *	优点:线程安全、调用效率高,但是不能延时加载。
 */
public class SingleTonDemo01 {
	//类初始化时,立即加载这个对象(没有延时加载的优势)。加载类时,天然的是线程安全的。
	private static SingleTonDemo01 object = new SingleTonDemo01();
	private SingleTonDemo01() {}
	//方法没有同步,调用效率会更高
	public static SingleTonDemo01 getSingleTon() {
		return object;
	}
}

这里在简单说明一下,我们可以看到这段代码,一上来就开始new 了一个对象。饿汉就在这里体现。
第二种:懒汉式实现单例设计

/**
 * 	懒汉式实现单例设计模式
 * @author tuofafa
 *	优点:线程安全、调用效率不高,但是可以延时加载
 */
public class SingleTonDemo02 {
	//单例对象延迟加载(类初始化时不加载这个对象,真正有用的时候才加载)
	private static SingleTonDemo02 object;
	private SingleTonDemo02() {} //私有化构造方法
	//使用了synchronized关键字,资源利用率提高了,但是并发效率降低了
	public static synchronized SingleTonDemo02 getSingleTon() {
		if(object == null) {
			object = new SingleTonDemo02();
		}
		return object;
	}
}

相比饿汉式而言,懒汉式在延时加载这块更拿手一点(延时加载就是,在类初始化的时候不用加载这个类,而在真正用的时候再去创建这个类)。
第三种:双重检测锁实现单例设计模式

/***
 * 	双重检测锁实现单例设计模式(结合了懒汉式和饿汉式的优点,但是由于
 * 	JVM底层偶尔会出现问题,不建议这样使用)
 * @author Administrator
 *
 */
public class SingleTonDemo03 {
	private static  SingleTonDemo03 instance;
	private SingleTonDemo03() {}
	public static SingleTonDemo03 getInstance() {
		if(instance == null) {
			synchronized (SingleTonDemo03.class) {
				if(instance == null) {
					instance = new SingleTonDemo03();		
				}
			}
		}
		return instance;
	}	
}

这种方式实现的单例设计模式不建议使用。
第四种:静态内部类实现单例设计模式

/***
 * 	静态内部类实现单例设计模式(真正实现线程安全、同步和效率比较高。可以延时加载)用的比较多 
 * @author tuofafa
 * 	优点:
 * 	外部类没有static属性,则不会像饿汉式那样立即加载对象
 * 	只有真正调用getInstance()的时候才会加载静态内部类。加载类时线程是安全的。
 */
 
public class SingleTonDemo04 {
	//这种方式:线程安全  调用效率比较高  延迟加载
	private static class SingleTonClassInstance{
		private static final SingleTonDemo04 instance = new SingleTonDemo04();
	}
	private SingleTonDemo04() {} //构造方法私有化
	//方法非同步,并发效率比较高
	public static SingleTonDemo04 getInstance() {
		return SingleTonClassInstance.instance;
	} 
}

该单例设计模式才是真正意义上结合了懒汉式和饿汉式的优点。真正实现了线程安全,调用效率比较高以及延迟加载。这也是经常用的一种。
第五种:枚举方式实现单例设计模式

/**
 * 	枚举实现单例设计模式(唯一的遗憾就是没有懒加载的功能)
 * @author tuofafa
 * 优点:实现简单    枚举本身就是单例模式,由JVM根本上保证,避免通过反射和反序列化的漏洞
 * 缺点:不能延时加载
 */
public enum SingleTonDemo05 {
	//这个枚举元素,本身就是单例对象
	INSTANCE;
	
	//添加自己需要的方法或者操作!
	public void singleTonOPertion() {
		System.out.println("Hello 天津");
	}
}

这种模式可以算得上实现单例设计中最简单的一种了。同时也是很有特点的一种。实现简单,还可以避免通过反射和反序列化的漏洞(这一点是前面四种都不能避开的)。学过反射我们都知道。因为反射的存在,打破了java这种私有化的平衡。原来可能我们都认为我一个类的属性和构造方法或者普通方法定义成private,外界就没有办法访问了。是的,那是因为还没有反射的存在。有了反射之后,即使你定义成私有化的也是可以访问的。
总结如下:

  • 主要:

     	饿汉式(线程安全  调用效率高  ,但是不能延时加载)
     	懒汉式(线程安全  调用效率不高  但是可以延时加载)
    
  • 其他:

     	双重检测锁式(由于JVM底层内部模型原因,偶尔会问题,不建议使用)
     	静态内部类式(线程安全 调用效率高  也可以延时加载),经常被使用
     	枚举单例(线程安全  调用效率高 但是不能延时加载,可以避免反射和反序列化的漏洞)
    

如何选用?

  • 单例对象 占用 资源 少, 不需要延时加载

     	枚举式 好于 饿汉式
    
  • 单例对象 占用 资源 大,需要延时加载

     	静态内部类 好于 懒汉式
    

今天的分享就到这里了,如果有什么疑问,欢迎各位评论……

你可能感兴趣的:(java)