深入解析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++中则一般使用懒汉式单例。
单例模式比较简单,在此就不举例代码演示了。

相关文章

  • Java结构型模式之门面模式详解

    Java结构型模式之门面模式详解

    门面模式又叫外观模式(Facade Pattern),主要用于隐藏系统的复杂性,并向客户端提供了一个客户端可以访问系统的接口,本文通过实例代码给大家介绍下java门面模式的相关知识,感兴趣的朋友一起看看吧
    2023-02-02
  • java 记录一个子串在整串中出现的次数实例

    java 记录一个子串在整串中出现的次数实例

    今天小编就为大家分享一篇java 记录一个子串在整串中出现的次数实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-07-07
  • logstash将mysql数据同步到elasticsearch方法详解

    logstash将mysql数据同步到elasticsearch方法详解

    这篇文章主要为大家介绍了logstash将mysql数据同步到elasticsearch方法详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-12-12
  • 使用Java模拟鼠标和键盘的详细操作步骤

    使用Java模拟鼠标和键盘的详细操作步骤

    这篇文章主要介绍了使用Java模拟鼠标和键盘的详细操作步骤,要运行上面提供的Java程序,您需要遵循几个步骤来设置Java环境、编写程序代码,并执行该程序,文中有相关的代码示例,需要的朋友可以参考下
    2024-05-05
  • SpringBoot+Vue实现动态菜单的思路梳理

    SpringBoot+Vue实现动态菜单的思路梳理

    这篇文章主要为大家详细介绍了利用SpringBoot+Vue实现动态菜单的思路梳理,文中的示例代码讲解详细,感兴趣的小伙伴可以动手尝试一下
    2022-07-07
  • springboot中如何使用openfeign进行接口调用

    springboot中如何使用openfeign进行接口调用

    这篇文章主要介绍了springboot中如何使用openfeign进行接口调用问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-07-07
  • javaweb上传下载实例完整版解析(下)

    javaweb上传下载实例完整版解析(下)

    这篇文章主要为大家详细解析了javaweb上传下载实例,本文重点在于文件下载功能的实现,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-01-01
  • java实战项目之记账软件

    java实战项目之记账软件

    这篇文章主要介绍了java实战项目之记账软件,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-04-04
  • spring boot定时器实现定时同步数据的操作步骤

    spring boot定时器实现定时同步数据的操作步骤

    在Java中,@Scheduled注解是用于指定定时任务的执行规则的,这篇文章给大家介绍spring boot定时器实现定时同步数据的操作步骤,感兴趣的朋友一起看看吧
    2023-12-12
  • Java实现一个达达租车系统的步骤详解

    Java实现一个达达租车系统的步骤详解

    这篇文章主要给大家介绍了利用Java实现一个达达租车系统的步骤,文中给出了详细的实现思路和示例代码,并在文末给出了完整的源码供大家学习下载,需要的朋友可以参考借鉴,下面来一起看看吧。
    2017-04-04

最新评论