Java设计模式——单例模式

原创 2016年08月30日 11:24:12

简介

java中,有些对象我们只需要一个,比如:配置文件、线程池、日志对象等。如果创造出多个实例,就会导致很多问题,比如占用过多资源、不一致的结果等。

单例模式的实现方式有多种,万变不离其宗,单例模式的中心思想就是构造方法私有化。也就是说不允许在类的外部创造实例,那么类的实例从哪来?类自己提供,具体来说是类中的类方法提供。主要的实现方式有两种:恶汉模式和懒汉模式。

恶汉模式与懒汉模式

代码

/**
 * 单例模式测试类
 * @author Goser	(mailto:goskalrie@163.com)
 * @Since 2016年8月30日
 */
public class Singleton {
	
	public static void main(String[] args) {
		SingletonHungry sh1 = SingletonHungry.getInstance();
		SingletonHungry sh2 = SingletonHungry.getInstance();
		compare(sh1, sh2);//true
		SingletonLazy sl1 = SingletonLazy.getInstance();
		SingletonLazy sl2 = SingletonLazy.getInstance();
		compare(sl1, sl2);//true
	}
	
	public static <T> void compare(T obj1, T obj2){
		System.err.println(obj1==obj2);
	}
}
//恶汉模式
class SingletonHungry{
	//1.将构造方法私有化,不允许外部直接创建对象
	private SingletonHungry(){}
	//2.创建类的唯一实例
	private static SingletonHungry singletonHungry =new SingletonHungry();
	//3.提供一个用于获取实例的方法
	public static SingletonHungry getInstance(){
		return singletonHungry;
	}
}

//懒汉模式
class SingletonLazy{
	private SingletonLazy(){}
	private static SingletonLazy singletonLazy;
	public static SingletonLazy getInstance(){
		if(singletonLazy == null){
			singletonLazy = new SingletonLazy();
		}
		return singletonLazy;
	}
}

上面的测试代码中都打印出了true,说明单例模式是成功的。

从上面的代码中不难总结,实现单例模式的主要步骤:

1.构造方法私有化;

2.声明或创造实例;

3.提供实例的方法。

代码讲解:

第一步使用private关键字,将构造方法私有化,也就是说不允许外部实例化该类,好多资料都将单例模式和古代的皇帝进行比较,那么这一步就相当于中央集权。

第二步static声明或实例化,既然外部没有实例化的权利,但是类只要是想要使用,那么就需要实例化,总要有个人做这件事,这里有类本身进行实例化。

第三步将第二步的实例返回给调用getInsance方法的外部。

更进一步

以上三步,每一步都很重要,为什么使用上面的三步就能实现单例模式呢?前面说的单例模式的中心思想是构造方法私有化,但是,底层关键的是第二步的static关键字。static关键字的特点是,被static修饰的变量一般称为类变量,没有被static修饰的变量一般称为实例变量。从这点就能看出,类变量是与类对应的,类是唯一的,那么类变量也应该是唯一的。实际上,被static修饰的类变量,JVM只为静态分配一次内存,在加载类的过程中完成静态变量的内存分配,也就是说,类变量的内存是静止的(但是内存中的内容是可以改变的,就像是房子,房子盖好后不能再随便拆掉再盖,但是里面的人可以变)。由static关键字的特点可以得出,SingletonHungry中的singletonHungry变量SingletonLazy中的singletonLazy变量在内存中是唯一的,以此来实现单例,步骤一中的构造方法私有化是保障,步骤二中的static才是实现关键。

恶汉模式VS懒汉模式

恶汉与懒汉的命名由其实例化的时机而来。恶汉模式是在类加载的时候就实例化,形象的说就是,该种模式比较饥饿,需要先吃饱(实例化)才能工作,而懒汉模式则是在调用getInstance方法时才实例化,相较恶汉比较懒。

恶汉模式的特点是加载类时比较慢,但运行时获取对象的速度比较快(线程安全),因为在类加载时就实例化好了,已经与线程无关了。

懒汉模式的特点是加载类时比较快,但运行时获取对象的速度比较慢(线程不安全)

在实际使用时,一般两者区分很小,在对线程安全有要求时才会加以区别,或是使用其他的变体。但是面试的时候,基本上写出中心的步骤就可以了,如果能将static的底层关键表述出来,远比多说几种变体好的多。(面试的时候不一定面试官问你什么你就说什么,可以适当的明修栈道暗渡陈仓……声东击西,往往会得到意外的收货,但是相关性是第一的,问你java你不能回答C吧,但是问你单例模式的几种实现方式,你只还记得恶汉模式,那么回答完恶汉模式的实现肯定是不行的,会扣分的,但是补充上底层的实现,绝对是加分项)。

懒汉模式的线程安全改进

synchronized

线程安全是个专门的课程,一般的是使用同步synchronized关键字

//懒汉模式
class SingletonLazy{
	private SingletonLazy(){}
	private static SingletonLazy singletonLazy;
	public static synchronized  SingletonLazy getInstance(){
		if(singletonLazy == null){
			singletonLazy = new SingletonLazy();
		}
		return singletonLazy;
	}
有些资料上还会介绍什么双重检查,如:

//public static SingletonLazy getInstance() {  
//if (singletonLazy == null) {    
//  synchronized (SingletonLazy.class) {    
//     if (SingletonLazy == null) {    
//        SingletonLazy = new SingletonLazy();   
//     }    
//  }    
//}    
//return singletonLazy;   
//}
但是根本没什么用的,所以所有的代码注释掉了,没有什么实际的作用,还影响性能。

静态内部类方式

//静态内部类方式实现的单例模式
class SingletonLazy2 {    
    private static class LazyHolder {
       private static final SingletonLazy2 INSTANCE = new SingletonLazy2();
    }
    private SingletonLazy2(){}
    public static final SingletonLazy2 getInstance() {
       return LazyHolder.INSTANCE;
    }
}
这种方式,实际上是什么呢?将开始的代码做以下改变:

class SingletonLazy{
	private static class SingletonHungry {
		private static final SingletonLazy SINGLETONLAZY = new SingletonLazy();
	}
	private SingletonLazy(){}
	public static final SingletonLazy getInstance(){
		return SingletonHungry.SINGLETONLAZY;
	}
}
经过对比不难发现,所谓的静态内部类方式只不过是将恶汉模式作为懒汉模式的静态内部类来使用了,将懒汉和恶汉的优点结合起来,就成了即线程安全,又不影响性能的静态内部类模式了。

总结

单例模式基本有两种实现方式:恶汉模式和懒汉模式,恶汉模式线程安全,懒汉模式不安全,但是可以通过修改实现线程安全,单例模式还有其他的实现方式,但是常用的就是上面讲到的。



版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

简述ava.lang.IllegalStateException异常发生的原因

jsp中本身有默认的getWriter()方法,如果你的程序的代码中有调用response对象的getOutputStream()方法的话就会重了,系统不知道到底调用哪个方法了,就会报ava.lang...

一文教你看懂大数据的技术生态圈:Hadoop,hive,spark

大数据本身是个很宽泛的概念,Hadoop生态圈(或者泛生态圈)基本上都是为了处理超过单机尺度的数据处理而诞生的。你可以把它比作一个厨房所以需要的各种工具。锅碗瓢盆,各有各的用处,互相之间又有重合。你可...
  • ynwso
  • ynwso
  • 2015年07月23日 14:10
  • 430

设计模式——单例模式

  • 2011年04月16日 21:55
  • 39KB
  • 下载

设计模式——单例模式

  • 2013年08月11日 14:59
  • 4KB
  • 下载

Java设计模式学习01——单例模式

转载自:http://blog.csdn.net/xu__cg/article/details/52902644 Java单例模式是一种常见且较为简单的设计模式。单例模式,顾名思义一个类仅能有一个实...

设计模式实现——单例模式

  • 2016年09月03日 19:28
  • 18.2MB
  • 下载

学习笔记——JAVA设计模式<1>单例模式

Group of four GFO23种设计模式 创建型模式建对象 单例模式 工厂模式 抽象工厂模式 建造者模式原型模式 结构性模式 适配器模式 桥接模式 装饰模式 组合模式...

Java设计模式之——单例模式

定义:Singleton(单例)模式主要作用是保证在Java应用程序中,一个类Class只有一个实例存在。 使用Singleton模式可以节省内存,因为它限制了实例的个数,有利于Java垃圾回收(ga...

Java面向对象设计模式(三)——单例模式

单例对象(Singleton)是一种常用的设计模式。在Java应用中,单例对象能保证在一个JVM中,该对象只有一个实例存在。这样的模式有几个好处: 1、某些类创建比较频繁,对于一些大型的对象,这...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Java设计模式——单例模式
举报原因:
原因补充:

(最多只允许输入30个字)