深入解析Java的设计模式编程中单例模式的使用

 更新时间:2016年02月03日 15:45:59   作者:卡奴达摩  
这篇文章主要介绍了深入解析Java的设计模式编程中单例模式的使用,一般来说将单例模式分为饿汉式单例和懒汉式单例,需要的朋友可以参考下

定义:确保一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。
类型:创建类模式
类图:

201623154414458.gif (258×145)

类图知识点:
1.类图分为三部分,依次是类名、属性、方法
2.以<<开头和以>>结尾的为注释信息
3.修饰符+代表public,-代表private,#代表protected,什么都没有代表包可见。
4.带下划线的属性或方法代表是静态的。
5.对类图中对象的关系不熟悉的朋友可以参考文章:设计模式中类的关系。
单例模式应该是23种设计模式中最简单的一种模式了。它有以下几个要素:

  • 私有的构造方法
  • 指向自己实例的私有静态引用
  • 以自己实例为返回值的静态的公有的方法

来看一个简单的例子:

package com.wolf.action;
import java.util.HashMap;
import java.util.Map;
public class demo {
 public static void main(String args[]) throws InstantiationException,
  IllegalAccessException, ClassNotFoundException {
 System.out.println(Son.getInstance().getName());
 System.out.println("我是谁");
 }
}
class Son extends Father {
 private String name = "儿子";
 final String CLASS = "demo";
 protected String getName() {
 return this.query("aaa");
 }
 public static Son getInstance() throws InstantiationException,
  IllegalAccessException, ClassNotFoundException {
 // 这里必须是全局路径 否则无法找到
 return (Son) instance("com.wolf.action.Son");
 }
}
class Father {
 private static Map<String, Object> instance = new HashMap<String, Object>();
 private String name = "父类";
 protected void Fatcher() {
 System.out.println("我是父类");
 }
 protected String query(String sql) {
 return sql + "has been done";
 }
 public static Object instance(String objname)
  throws InstantiationException, IllegalAccessException,
  ClassNotFoundException {
 if (instance.get(objname) == null
  || !(instance.get(objname) instanceof Father)) {
  instance.put(objname, Class.forName(objname).newInstance());
 }
 return instance.get(objname);
 }
}

        单例模式根据实例化对象时机的不同分为两种:一种是饿汉式单例,一种是懒汉式单例。饿汉式单例在单例类被加载时候,就实例化一个对象交给自己的引用;而懒汉式在调用取得实例方法的时候才会实例化对象。代码如下:
饿汉式单例

public class Singleton { 
  private static Singleton singleton = new Singleton(); 
  private Singleton(){} 
  public static Singleton getInstance(){ 
    return singleton; 
  } 
} 

懒汉式单例

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

单例模式的优点:

  • 在内存中只有一个对象,节省内存空间。
  • 避免频繁的创建销毁对象,可以提高性能。
  • 避免对共享资源的多重占用。
  • 可以全局访问。

适用场景:由于单例模式的以上优点,所以是编程中用的比较多的一种设计模式。我总结了一下我所知道的适合使用单例模式的场景:

  • 需要频繁实例化然后销毁的对象。
  • 创建对象时耗时过多或者耗资源过多,但又经常用到的对象。
  • 有状态的工具类对象。
  • 频繁访问数据库或文件的对象。
  • 以及其他我没用过的所有要求只有一个对象的场景。

单例模式注意事项:

  • 只能使用单例类提供的方法得到单例对象,不要使用反射,否则将会实例化一个新对象。
  • 不要做断开单例类对象与类中静态引用的危险操作。
  • 多线程使用单例使用共享资源时,注意线程安全问题。

关于java中单例模式的一些争议:

单例模式的对象长时间不用会被jvm垃圾收集器收集吗
        看到不少资料中说:如果一个单例对象在内存中长久不用,会被jvm认为是一个垃圾,在执行垃圾收集的时候会被清理掉。对此这个说法,笔者持怀疑态度,笔者本人的观点是:在hotspot虚拟机1.6版本中,除非人为地断开单例中静态引用到单例对象的联接,否则jvm垃圾收集器是不会回收单例对象的。
对于这个争议,笔者单独写了一篇文章进行讨论,如果您有不同的观点或者有过这方面的经历请进入文章单例模式讨论篇:单例模式与垃圾收集参与讨论。
 
在一个jvm中会出现多个单例吗
        在分布式系统、多个类加载器、以及序列化的的情况下,会产生多个单例,这一点是无庸置疑的。那么在同一个jvm中,会不会产生单例呢?使用单例提供的getInstance()方法只能得到同一个单例,除非是使用反射方式,将会得到新的单例。代码如下

Class c = Class.forName(Singleton.class.getName()); 
Constructor ct = c.getDeclaredConstructor(); 
ct.setAccessible(true); 
Singleton singleton = (Singleton)ct.newInstance(); 

这样,每次运行都会产生新的单例对象。所以运用单例模式时,一定注意不要使用反射产生新的单例对象。
 
懒汉式单例线程安全吗
        主要是网上的一些说法,懒汉式的单例模式是线程不安全的,即使是在实例化对象的方法上加synchronized关键字,也依然是危险的,但是笔者经过编码测试,发现加synchronized关键字修饰后,虽然对性能有部分影响,但是却是线程安全的,并不会产生实例化多个对象的情况。
 
单例模式只有饿汉式和懒汉式两种吗
        饿汉式单例和懒汉式单例只是两种比较主流和常用的单例模式方法,从理论上讲,任何可以实现一个类只有一个实例的设计模式,都可以称为单例模式。
 
单例类可以被继承吗
        饿汉式单例和懒汉式单例由于构造方法是private的,所以他们都是不可继承的,但是其他很多单例模式是可以继承的,例如登记式单例。
 
饿汉式单例好还是懒汉式单例好
        在java中,饿汉式单例要优于懒汉式单例。C++中则一般使用懒汉式单例。
单例模式比较简单,在此就不举例代码演示了。

相关文章

  • SpringBoot整合ES-Elasticsearch的实例

    SpringBoot整合ES-Elasticsearch的实例

    这篇文章主要介绍了SpringBoot整合ES-Elasticsearch的实例,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-05-05
  • Java中的Collections类的使用示例详解

    Java中的Collections类的使用示例详解

    Collections类提供了一些静态方法,这些方法能够对List集合实现常用的算法操作,这些算法是排序,填充,移位和查找等。本文将通过示例为大家详细讲讲Collections类的使用,需要的可以参考一下
    2022-12-12
  • Java线程优先级示例代码

    Java线程优先级示例代码

    使用过Bit下载软件的同学应该很清楚,我们有多个下载任务同时执行,而其中的某一个或多个是非常重要的,于是给这些任务设定一个高度优先,以便任务可以获取更多的带宽尽早完成下载
    2013-09-09
  • Netty内存池泄漏问题以解决方案

    Netty内存池泄漏问题以解决方案

    这篇文章主要介绍了Netty内存池泄漏问题以解决方案,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-12-12
  • Spring Boot 分库分表策略示例展示

    Spring Boot 分库分表策略示例展示

    分库分表是为了应对大规模数据和高并发请求,提高系统的性能和可扩展性,以下是如何在 Spring Boot 中实现分库分表的详细策略,感兴趣的朋友一起看看吧
    2024-08-08
  • java安全编码指南之:声明和初始化说明

    java安全编码指南之:声明和初始化说明

    这篇文章主要介绍了java安全编码指南之:声明和初始化说明,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-09-09
  • kotlin和Java的相互调用示例详解

    kotlin和Java的相互调用示例详解

    Kotlin 的设计过程中就考虑到了与 Java 的互操作性。在 Kotlin 中可以直接调用既有的 Java 代码, 反过来在 Java 中也可以很流畅地使用 Kotlin 代码,下面这篇文章主要给大家介绍了关于kotlin和Java的相互调用的相关资料,需要的朋友可以参考下。
    2018-02-02
  • Java实现猜数字小游戏详解流程

    Java实现猜数字小游戏详解流程

    猜数字是兴起于英国的益智类小游戏,起源于20世纪中期,一般由两个人或多人玩,也可以由一个人和电脑玩。游戏规则为一方出数字,一方猜,今天我们来用Java把这个小游戏写出来练练手
    2021-10-10
  • 十种JAVA排序算法实例

    十种JAVA排序算法实例

    本文件讲了十种JAVA排序方法(冒泡(Bubble)排序——相邻交换 、选择排序——每次最小/大排在相应的位置 、插入排序——将下一个插入已排好的序列中 、壳(Shell)排序——缩小增量 、归并排序 、快速排序 、堆排序 、拓扑排序 、锦标赛排序 、基数排序)的使用,并提供了实例代码可参考
    2013-11-11
  • 使用maven插件对java工程进行打包过程解析

    使用maven插件对java工程进行打包过程解析

    这篇文章主要介绍了使用maven插件对java工程进行打包过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-08-08

最新评论