一. 什么是单例模式

只需要某个类同时保留一个对象,不希望有更多对象,此时,我们则应考虑单例模式的设计。

单例模式的主要作用是保证在Java程序中,某个类只有一个实例存在。

单例模式有很多好处,它能够避免实例对象的重复创建,不仅可以减少每次创建对象的时间开销,还可以节约内存空间;

能够避免由于操作多个实例导致的逻辑错误。如果一个对象有可能贯穿整个应用程序,而且起到了全局统一管理控制的作用,那么单例模式也许是一个值得考虑的选择。

二. 单例模式的特点

1. 单例模式只能有一个实例。

2. 单例类必须创建自己的唯一实例。

3. 单例类必须向其他对象提供这一实例。

三. 单例模式VS静态类

在知道了什么是单例模式后,我想你一定会想到静态类,“既然只使用一个对象,为何不干脆使用静态类?”,这里我会将单例模式和静态类进行一个比较。

1. 单例可以继承和被继承,方法可以被override,而静态方法不可以。

2. 静态方法中产生的对象会在执行后被释放,进而被GC清理,不会一直存在于内存中。

3. 静态类会在第一次运行时初始化,单例模式可以有其他的选择,即可以延迟加载。

4. 基于2, 3条,由于单例对象往往存在于DAO层(例如sessionFactory),如果反复的初始化和释放,则会占用很多资源,而使用单例模式将其常驻于内存可以更加节约资源。

5. 静态方法有更高的访问效率。

6. 单例模式很容易被测试。

几个关于静态类的误解:

误解一:静态方法常驻内存而实例方法不是。

实际上,特殊编写的实例方法可以常驻内存,而静态方法需要不断初始化和释放。

误解二:静态方法在堆(heap)上,实例方法在栈(stack)上。

实际上,都是加载到特殊的不可写的代码内存区域中。

静态类和单例模式情景的选择:

情景一:不需要维持任何状态,仅仅用于全局访问,此时更适合使用静态类。

情景二:需要维持一些特定的状态,此时更适合使用单例模式。

单例模型的写法

1、饿汉模式

public class Singleton{
private static Singleton instance = new Singleton();
private Singleton(){}
public static Singleton newInstance(){
return instance;
}
}

注解:初试化静态的instance创建一次。如果我们在Singleton类里面写一个静态的方法不需要创建实例,它仍然会早早的创建一次实例。而降低内存的使用率。

缺点:没有lazy loading的效果,从而降低内存的使用率。

2、懒汉模式

public class Singleton{
private static Singleton instance = null;
private Singleton(){}
public static Singleton newInstance(){
if(null == instance){
instance = new Singleton();
}
return instance;
}
}

注解:Singleton的静态属性instance中,只有instance为null的时候才创建一个实例,构造函数私有,确保每次都只创建一个,避免重复创建。
缺点:只在单线程的情况下正常运行,在多线程的情况下,就会出问题。例如:当两个线程同时运行到判断instance是否为空的if语句,并且instance确实没有创建好时,那么两个线程都会创建一个实例,因此需要加锁解决线程同步问题,实现如下:

public class Singleton{
private static Singleton instance = null;
private Singleton(){}
public static synchronized Singleton newInstance(){
if(null == instance){
instance = new Singleton();
}
return instance;
}
}

3、双重校验锁 

public class Singleton {
private static Singleton instance = null;
private Singleton(){}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {//2
instance = new Singleton();
}
}
}
return instance;
}
}

注解:只有当instance为null时,需要获取同步锁,创建一次实例。当实例被创建,则无需试图加锁。
缺点:用双重if判断,复杂,容易出错。

4、静态内部类

public class Singleton{
private static class SingletonHolder{
public static Singleton instance = new Singleton();
}
private Singleton(){}
public static Singleton newInstance(){
return SingletonHolder.instance;
}
}

这种方式同样利用了类加载机制来保证只创建一个instance实例。它与饿汉模式一样,也是利用了类加载机制,因此不存在多线程并发的问题。不一样的是,它是在内部类里面去创建对象实例。这样的话,只要应用中不使用内部类,JVM就不会去加载这个单例类,也就不会创建单例对象,从而实现懒汉式的延迟加载。也就是说这种方式可以同时保证延迟加载和线程安全。

5、枚举

public enum Singleton{
instance;
public void whateverMethod(){}
}  

上面提到的四种实现单例的方式都有共同的缺点:

1)需要额外的工作来实现序列化,否则每次反序列化一个序列化的对象时都会创建一个新的实例。

2)可以使用反射强行调用私有构造器(如果要避免这种情况,可以修改构造器,让它在创建第二个实例的时候抛异常)。

而枚举类很好的解决了这两个问题,使用枚举除了线程安全和防止反射调用构造器之外,还提供了自动序列化机制,防止反序列化的时候创建新的对象。因此,《Effective Java》作者推荐使用的方法。不过,在实际工作中,很少看见有人这么写。

总结

本文总结了五种Java中实现单例的方法,其中前两种都不够完美,双重校验锁和静态内部类的方式可以解决大部分问题,平时工作中使用的最多的也是这两种方式。枚举方式虽然很完美的解决了各种问题,但是这种写法多少让人感觉有些生疏。个人的建议是,在没有特殊需求的情况下,使用第三种和第四种方式实现单例模式。

【Java】设计模型-五种单例模型的更多相关文章

  1. Java五种单例区别

    详细请参考如下链接: http://www.voidcn.com/article/p-shzgsluz-bqa.html https://blog.csdn.net/android_freshman/ ...

  2. Java学习笔记之---单例模型

    Java学习笔记之---单例模型 单例模型分为:饿汉式,懒汉式 (一)要点 1.某个类只能有一个实例 2.必须自行创建实例 3.必须自行向整个系统提供这个实例 (二)实现 1.只提供私有的构造方法 2 ...

  3. 转:Windows Socket五种I/O模型

    原文转自:  Windows Socket五种I/O模型 Winsock 的I/O操作: 1. 两种I/O模式 阻塞模式:执行I/O操作完成前会一直进行等待,不会将控制权交给程序.套接字 默认为阻塞模 ...

  4. Windows Socket五种I/O模型——代码全攻略(转)

    Winsock 的I/O操作: 1. 两种I/O模式 阻塞模式:执行I/O操作完成前会一直进行等待,不会将控制权交给程序.套接字 默认为阻塞模式.可以通过多线程技术进行处理. 非阻塞模式:执行I/O操 ...

  5. 五种I/O模型的学习

    来自   http://www.52im.net/thread-1935-1-1.html 4.互联网服务端处理网络请求的原理 首先看看一个典型互联网服务端处理网络请求的典型过程:<ignore ...

  6. Windows Socket五种I/O模型

    转载:http://www.cnblogs.com/tianzhiliang/archive/2010/08/31/1813637.html 如果你想在Windows平台上构建服务器应用,那么I/O模 ...

  7. I/O模型之一:Unix的五种I/O模型

    目录: <I/O模型之一:Unix的五种I/O模型> <I/O模型之二:Linux IO模式及 select.poll.epoll详解> <I/O模型之三:两种高性能 I ...

  8. 你可以这么理解五种I/O模型

    因为项目需要,接触和使用了Netty,Netty是高性能NIO通信框架,在业界拥有很好的口碑,但知其然不知其所以然. 所以本系列文章将从基础开始学起,深入细致的学习NIO.本文主要是介绍五种I/O模型 ...

  9. 五种典型开发周期模型(瀑布、V、原型化、螺旋、迭代)

    五种典型开发周期模型(瀑布.V.原型化.螺旋.迭代) 总结一下经常可以见到的系统开发周期模型.    在过去的几年里,可以很奇葩的碰到类似于“创业项目库”这种需求非常明确,工作量十分可控,对质量要求比 ...

随机推荐

  1. XamarinEssentials教程清空键值

    XamarinEssentials教程清空键值 Preferences类的Clear()方法可以清空所有的键和值.该方法有两种形式,下面依次进行介绍. (1)Clear()方法的语法形式如下: pub ...

  2. AE视频制作 参考 资源 科幻科技风格 开场 公司企业宣传

    == 人类已经堕落,机器军团大举入侵,开始抹除人类,净化地球!-原创-高清正版视频在线观看–爱奇艺 这个视频片头不错.展示效果和音乐. == 外星人入侵地球,只为收集人类大脑!速看科幻电影<天际 ...

  3. Python3基础-分数运算

    Python3分数运算 fractions 模块可以被用来执行包含分数的数学运算. 案例 >>> from fractions import Fraction >>> ...

  4. Stm32常见英文缩写

    Stm32常见英文缩写 https://wenku.baidu.com/view/4b9c2eee5022aaea998f0f5b.html STM32嵌入式开发常见缩写 https://wenku. ...

  5. [CC-SEAPERM2]Sereja and Permutations

    [CC-SEAPERM2]Sereja and Permutations 题目大意: 有一个\(n(n\le300)\)排列\(p\),将其中一个元素\(p_i\)拿掉,然后将原来大于\(p_i\)的 ...

  6. Spring MVC ,使用mvc:resources标签后,处理器无法被访问

    在SpringMVC的配置文件中添加了<mvc:resources mapping="/img/**" location="/img/"/>以便处理 ...

  7. 修改Chrome启动参数解决跨域问题

    这个做法仅仅是针对自己本机,只是一个权宜方案 --disable-web-security --user-data-dir=本地用户信息目录 之后启动Chrome浏览器即可

  8. Python实现图像信息隐藏

    Python实现图像信息隐藏 之前学习密码学的时候老师有提到过『信息隐藏』,现在用图像的方法尝试一下.思想是:把信息藏到RGB通道中的B通道,然后利用奇偶性可以恢复过来 原理 从源图中提取文字图像信息 ...

  9. Node爬取简书首页文章

    Node爬取简书首页文章 博主刚学node,打算写个爬虫练练手,这次的爬虫目标是简书的首页文章 流程分析 使用superagent发送http请求到服务端,获取HTML文本 用cheerio解析获得的 ...

  10. Prior Posterior和Likelihood的理解与几种表达方式