单例模式(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. Python第八天

    Python面向对象进阶 一.静态方法 通过@staticmethod装饰器即可把其装饰的方法变为一个静态方法,什么是静态方法呢?其实不难理解,普通的方法,可以在实例化后直接调用,并且在方法里可以通过 ...

  2. C# 读取和配置IniFile

    自定义IniFile操作类 using System; using System.Collections.Generic; using System.Linq; using System.Text; ...

  3. Codeforces Round #381 (Div. 2)A. Alyona and copybooks(dfs)

    A. Alyona and copybooks Problem Description: Little girl Alyona is in a shop to buy some copybooks f ...

  4. pjax技术的应用

    一.什么是PJAX? 现在有一些网站(apicloud,  github)支持这样一种浏览方式,当你点击站内的一个连接的时候,不是传统的跳转到另外一个连接,而是类似ajax的局部刷新改变页面内容,但是 ...

  5. Java数组在内存中是如何存放的

    阅读目录 一维数组 二维数组 数组对象及其引用存放在内存中的哪里? Java中有两种类型的数组: 基本数据类型数组: 对象数组: 当一个对象使用关键字“new”创建时,会在堆上分配内存空间,然后返回对 ...

  6. c++ 一些随笔

    1. A region of source code where any use of the unqualified name (that is, as a plain identifier) re ...

  7. 通过seekBar改变图片的透明度

    作者:堕落的天使 对应的图片 activity_main.xml(代码) <RelativeLayout xmlns:android="http://schemas.android.c ...

  8. JS获取form表单所有属性值

    // 得到一个表单里的全部信息function getFormQueryString() { var frmID=document.forms[0]; var i,queryString=" ...

  9. Git Pro - (2)分支

    Git 保存的不是文件差异或者变化量,而只是一系列文件快照. 在 Git中提交时,会保存一个提交(commit)对象,它包含一个指向暂存内容快照的指针,作者和相关附属信息,以及一定数量(也可能没有)指 ...

  10. Lucene.net 多条件查询搜索

    最近一直在研究lucene,目的是想让网站实现像搜索引擎那样的搜索,可以快速.准确的帮用户查询出想要的结果.废话不多说,上代码实例: 1.利用BooleanQuery进行多条件搜索(比较灵活)   L ...