单例模式(Singleton),保证一个类仅有一个实例,并提供一个访问它的全局访问点。

 

使用场景:在一个系统中,要求一个类有且仅有一个对象,如果出现多个对象就会出现“不良反映”,可以使用单例模式。例如:在计算机系统中,线程池、缓存、日志对象、web计数器等 常被设计成单例。这些应用都或多或少具有资源管理器的功能。

 

优点:

(1)单例模式在内存中只有一个实例,减少了内存开支,特别是一个对象需要频繁地创建、销毁时,而且创建或销毁时性能又无法优化, 单例模式的优势就非常明显。

(2)单例模式只生成一个实例,所以减少了系统的性能开销,当一个对象的产生需要比较多的资源时,如读取配置、产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象, 然后用永久驻留内存的方式来解决(在Java EE中采用单例模式时需要注意JVM垃圾回收机制)。

(3)单例模式可以避免对资源的多重占用,例如一个写文件动作,由于只有一个实例存在内存中,避免对同一个资源文件的同时写操作。

(4)单例模式可以在系统设置全局的访问点,优化和共享资源访问,例如可以设计一个单例类,负责所有数据表的映射处理。

缺点:

(1)单例模式一般没有接口,扩展很困难,若要扩展,除了修改代码基本上没有第二种途径可以实现。单例模式为什么不能增加接口呢?因为接口对单例模式是没有任何意义的,它要求“自行实例化”,并且提供单一实例、接口或抽象类是不可能被实例化的。当然,在特殊情况下,单例模式可以实现接口、被继承等,需要在系统开发中根据环境判断。

(2)单例模式对测试是不利的。在并行开发环境中,如果单例模式没有完成,是不能进行测试的,没有接口也不能使用mock的方式虚拟一个对象。

(3)单例模式与单一职责原则有冲突。一个类应该只实现一个逻辑,而不关心它是否是单例的,是不是要单例取决于环境,单例模式把“要单例”和业务逻辑融合在一个类中。

(摘自《设计模式之禅》)

 

单例模式一般分为懒汉式和饿汉式两种:

区别:

饿汉式:在类加时就创建对象,线程是安全的,但是没有延迟加载,如果没有使用就是浪费内存。

懒汉式:在类加载的时候不创建,而是在实际用到时创建,线程不安全(故需加上synchronized),但具有延迟加载的特性。

实现代码如下:

饿汉式:

public class SingletonDemo1 {

    //类初始化时,立即加载这个对象(没有延时加载的优势)。加载类时,天然的是线程安全的!
private static SingletonDemo1 instance = new SingletonDemo1(); private SingletonDemo1(){
} //方法没有同步,调用效率高!
public static SingletonDemo1 getInstance(){
return instance;
}
}

懒汉式:

public class SingletonDemo2 {

    //类初始化时,不初始化这个对象(延时加载,真正用的时候再创建)。
private static SingletonDemo2 instance; private SingletonDemo2(){ //私有化构造器
} //方法同步,调用效率低!
public static synchronized SingletonDemo2 getInstance(){
if(instance==null){
instance = new SingletonDemo2();
}
return instance;
} }

 

怎么能同时具有以上两种模式的共同特点呢?

常用的方式有两种:(1)双重检查锁实现单例模式 (2)静态内部类实现单例模式

双重检查锁实现单例模式由于JVM编译器的原因,有的时候会出现问题(不建议使用),代码如下,仅做参考:

public class SingletonDemo3 { 

  private static SingletonDemo3 instance = null; 

  public static SingletonDemo3 getInstance() {
if (instance == null) {
SingletonDemo3 sc;
synchronized (SingletonDemo3.class) {
sc = instance;
if (sc == null) {
synchronized (SingletonDemo3.class) {
if(sc == null) {
sc = new SingletonDemo3();
}
}
instance = sc;
}
}
}
return instance;
} private SingletonDemo3() { }
}

静态内部类实现单例模式(建议使用这种方式),代码如下:

/**
* 这种方式:线程安全,调用效率高,并且实现了延时加载!
*/
public class SingletonDemo4 { private static class SingletonClassInstance {
private static final SingletonDemo4 instance = new SingletonDemo4();
} private SingletonDemo4(){
} //方法没有同步,调用效率高!
public static SingletonDemo4 getInstance(){
return SingletonClassInstance.instance;
} }

 

还有一种是使用枚举的方式实现单例模式(我没有实际用过),这里代码给大家参考:

/**
* 枚举式实现单例模式(没有延时加载)
*/
public enum SingletonDemo5 { //这个枚举元素,本身就是单例对象!
INSTANCE; //添加自己需要的操作!
public void singletonOperation(){
} }

 

从严格的意义上来说把构造器私有后,并不是就不能再创建它的实例了,还可以通过其他的方式进行实例化。这里就和java中创建对象的几种方式相关。

在java中有四种创建对象的方式:

(1)通过构造器直接new

(2)可以通过clone方法创建

(3)通过反射的方式进行创建

(4)通过反序列化的方式创建

这样,虽然私有了构造器,但是还是可以通过反射和反序列化的方式创建对象。

为了防止通过反射的方式直接创建对象破坏单例模式的结构,可以在构造器中添加判断抛出异常,如下:

private SingletonDemo6(){ //私有化构造器
if(instance!=null){
throw new RuntimeException();
}
}

为了防止通过反序列化的方式创建对象破坏单例模式的结构,可以在复写readResolve个方法,这样在反序列化的时候直接调用这个方法,如下:

//反序列化时,如果定义了readResolve()则直接返回此方法指定的对象。而不需要单独再创建新对象!
private Object readResolve() throws ObjectStreamException {
return instance;
}

这样,基本就保证了单例模式的可靠行了。

附上完整的防止反射和反序列化创建对象的代码:

import java.io.ObjectStreamException;
import java.io.Serializable; /**
* 懒汉式单例模式(如何防止反射和反序列化漏洞)
*/
public class SingletonDemo6 implements Serializable {
//类初始化时,不初始化这个对象(延时加载,真正用的时候再创建)。
private static SingletonDemo6 instance; private SingletonDemo6(){ //私有化构造器
if(instance!=null){
throw new RuntimeException();
}
} //方法同步,调用效率低!
public static synchronized SingletonDemo6 getInstance(){
if(instance==null){
instance = new SingletonDemo6();
}
return instance;
} //反序列化时,如果定义了readResolve()则直接返回此方法指定的对象。而不需要单独再创建新对象!
private Object readResolve() throws ObjectStreamException {
return instance;
} }

 

扩展,如果一个类可以产生多个对象,对象的数量不受限制,则是非常容易实现的,直接使用new关键字就可以了,如果只需要一个对象,使用单例模式就可以了,但是如果要求一个类只能产生两三个对象呢?该怎么实现?

这里可以对单例模式进行扩展,如下:

public class SingletonDemo7 {

    //定义最多能产生的实例数量
private static int maxNumOfSingletonDemo = 2;
//定义一个列表,容纳所有的实例
private static ArrayList<SingletonDemo7> SingletonDemoList=new ArrayList<SingletonDemo7>();
//当前序列号
private static int countNumOfSingletonDemo =0;
//产生所有的对象
static{
for(int i=0;i<maxNumOfSingletonDemo;i++){
SingletonDemoList.add(new SingletonDemo7());
}
} private SingletonDemo7(){
} //随机获得一个对象
public static SingletonDemo7 getInstance(){
Random random = new Random();
//随机
countNumOfSingletonDemo = random.nextInt(maxNumOfSingletonDemo);
return SingletonDemoList.get(countNumOfSingletonDemo);
} }

java单例模式的更多相关文章

  1. 用java单例模式实现面板切换

    1.首先介绍一下什么是单例模式: java单例模式是一种常见的设计模式,那么我们先看看懒汉模式: public class Singleton_ { //设为私有方法,防止被外部类引用或实例 priv ...

  2. 【深入】java 单例模式(转)

    [深入]java 单例模式 关于单例模式的文章,其实网上早就已经泛滥了.但一个小小的单例,里面却是有着许多的变化.网上的文章大多也是提到了其中的一个或几个点,很少有比较全面且脉络清晰的文章,于是,我便 ...

  3. 深入Java单例模式(转)

    深入Java单例模式 源自 http://devbean.blog.51cto.com/448512/203501 在GoF的23种设计模式中,单例模式是比较简单的一种.然而,有时候越是简单的东西越容 ...

  4. Java 单例模式的七种写法

    Java 单例模式的七种写法 第一种(懒汉,线程不安全) public class Singleton { private static Singleton instance; private Sin ...

  5. java单例模式之懒汉式分析

    转自:http://blog.csdn.net/withiter/article/details/8140338 今天中午闲着没事,就随便写点关于Java单例模式的.其实单例模式实现有很多方法,这里我 ...

  6. Java 单例模式探讨

    以下是我再次研究单例(Java 单例模式缺点)时在网上收集的资料,相信你们看完就对单例完全掌握了 Java单例模式应该是看起来以及用起来简单的一种设计模式,但是就实现方式以及原理来说,也并不浅显哦. ...

  7. 单例模式:Java单例模式的几种写法及它们的优缺点

    总结下Java单例模式的几种写法: 1. 饿汉式 public class Singleton { private static Singleton instance = new Singleton( ...

  8. 9种Java单例模式详解(推荐)

    单例模式的特点 一个类只允许产生一个实例化对象. 单例类构造方法私有化,不允许外部创建对象. 单例类向外提供静态方法,调用方法返回内部创建的实例化对象.  懒汉式(线程不安全) 其主要表现在单例类在外 ...

  9. 你真的理解了java单例模式吗?讲别人都忽略的细节!

    前言:老刘这篇文章敢做保证,java的单例模式讲的比大多数的技术博客都要好,讲述别人技术博客都没有的细节!!! 1 java单例模式 直接讲实现单例模式的两种方法:懒汉式和饿汉式,单例模式的概念自己上 ...

  10. Java 单例模式详解

    概念: java中单例模式是一种常见的设计模式,单例模式分三种:懒汉式单例.饿汉式单例.登记式单例三种. 单例模式有一下特点: 1.单例类只能有一个实例. 2.单例类必须自己自己创建自己的唯一实例. ...

随机推荐

  1. Adobe 软件防止联网激活更改Hosts文件

    来自为知笔记(Wiz) 附件列表

  2. 使用spring连接及操作mongodb3.0

    前边有一篇记录过不使用spring,直接在java代码中连接和操作mongodb数据库,这里就紧随其后记录一下使用spring的情况下,在java中简单操作mongodb.   maven导包配置: ...

  3. 小例子 熟悉jquery

    <div class="tab-head"> <h2 id="tab1" class="selected">JQGr ...

  4. jquery autocomplete实现读取sql数据库自动补全TextBox

    转自我本良人 原文 jquery autocomplete实现读取sql数据库自动补全TextBox 项目需要这样子一个功能,其他部门提的意见,只好去实现了哦,搞了好久才弄出来,分享一下. 1.前台页 ...

  5. Unity UI on the HoloLens

    Following the steps under "Required configuration" will allow Unity UI to continue to work ...

  6. Sql Server中Float格式转换字符串varchar方法(转)

    1.[Sql Server](70)  SELECT CONVERT(varchar(100), CAST(@testFloat AS decimal(38,2)))SELECT STR(@testF ...

  7. log4cplus 在配置文件中设置文件路径,程序自动创建目录,且在日志文件前按日期创建相应的目录

    #include <string> #include <cstdio> #include <log4cplus/logger.h> #include <log ...

  8. XE6 & IOS开发之开发者账号、苹果证书(2):关于苹果证书

    网上能找到的关于Delphi XE系列的移动开发的相关文章甚少,本文尽量以详细的图文内容.傻瓜式的表达来告诉你想要的答案. 原创作品,请尊重作者劳动成果,转载请注明出处!!! 1.关于苹果证书. 注意 ...

  9. robocopy 命令小结

    robocopy "C:\dira" "J:\dira" /E /COPYALL /XJ /XD "C:\dira\dir1" " ...

  10. Android开发-无法新建Activity及新建后编译错误

    下载了其他的工程后,新建Activity时无法进行下一步,报错:“This template requires a minimum SDK version of at least 7, and the ...