『设计模式』我就要一个对象,你别给我这么多好不好!---单例模式

引入

作为一个现代社会文明青年,我觉得一夫一妻制非常合理。有些男人富裕了点,就想多照顾几个女人的行为,真的不可取,有的时候法律在这些面前显得难以生效,毕竟重婚罪又不能限制婚外情,多找几个对象。
在这里插入图片描述
人尚且如此,何况程序呢,面对只能实例化一个对象的程序,我们该如何处理呢?
我们今天就来看一下单例模式!
在这里插入图片描述

单例模式

单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。

注意:

  • 单例类只能有一个实例。
  • 单例类必须自己创建自己的唯一实例。
  • 单例类必须给所有其他对象提供这一实例。

目的:

保证一个类仅有一个实例,并提供一个访问它的全局访问点。

主要解决:

一个全局使用的类频繁地创建与销毁。

何时使用:

当您想控制实例数目,节省系统资源的时候。

优点

实例控制
单例模式会阻止其他对象实例化其自己的单例对象的副本,从而确保所有对象都访问唯一实例。
灵活性:
因为类控制了实例化过程,所以类可以灵活更改实例化过程。

缺点

开销:
虽然数量很少,但如果每次对象请求引用时都要检查是否存在类的实例,将仍然需要一些开销。可以通过使用静态初始化解决此问题。
可能的开发混淆:
使用单例对象(尤其在类库中定义的对象)时,开发人员必须记住自己不能使用new关键字实例化对象。因为可能无法访问库源代码,因此应用程序开发人员可能会意外发现自己无法直接实例化此类。
对象生存期:
不能解决删除单个对象的问题。在提供内存管理的语言中(例如基于.NET Framework的语言),只有单例类能够导致实例被取消分配,因为它包含对该实例的私有引用。在某些语言中(如 C++),其他类可以删除对象实例,但这样会导致单例类中出现悬浮引用。
不符合单一职责原则
没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。

实现:

单例模式的 UML 图
在这里插入图片描述

创建一个 Singleton 类

package 单例模式;

public class Singleton {
	private static Singleton singleobj;

	private  Singleton() {}//通过private限定,不能通过new建立对象,也不能继承(可以有继承操作,但是无意义)
	public  Singleton newinstance()
	{
		if(singleobj!=null) return singleobj;
		else {
			singleobj=new Singleton();
			return singleobj;
		}
	}
	 
	public void dosomething()
	{
		System.out.println("一个女朋友就够了!!!");
	}
	
	
}

从 singleton 类获取唯一的对象

package 单例模式;

public class SingletonTest {

	public static void main(String[] args) {
		// SingleObject object = new SingleObject();
		// 编译时错误:构造函数 SingleObject() 是不可见的

		Singleton SingleT = Singleton.newinstance();      //获取唯一可用的对象
		SingleT.dosomething();

	}

}

执行程序,输出结果:

一个女朋友就够了!!!

进阶

看完前面的实列,但是并不能真正的应用在实际应用中,因为在现实的中更多的涉及到线程的问题,所以,给大家展示一下以下的方法。

1. 懒汉式(线程不安全,不支持多线程)

对比:

  • 是否 Lazy 初始化:是
  • 是否多线程安全:否
  • 实现难度:易

描述: 这种方式是最基本的实现方式,也就是前面提到的方式,这种实现最大的问题就是不支持多线程。因为没有加锁 synchronized,所以严格意义上它并不算单例模式。
这种方式 lazy loading 很明显,不要求线程安全,在多线程不能正常工作,在多线程系统的今天,一般很少使用。

实例

package 单例模式;

public class Singleton {
	private static Singleton singleobj;
	
	private  Singleton() {}//通过private限定,不能通过new建立对象,也不能继承(可以有继承操作,但是无意义)
	
	public  Singleton newinstance(){
		if(singleobj!=null) return singleobj;
		else {
			singleobj=new Singleton();
			return singleobj;
		}
	}
	
	public void dosomething()	{
		System.out.println("一个女朋友就够了!!!");
	}
}
2. 懒汉式(线程安全,支持多线程)

对比:

  • 是否 Lazy 初始化:是
  • 是否多线程安全:是
  • 实现难度:易

描述: 这种方式具备很好的 lazy loading,能够在多线程中很好的工作,但是,效率很低,99% 情况下不需要同步,可以说是一种以时间换空间的方法。

  • 优点:
    第一次调用才初始化,避免内存浪费。
  • 缺点:
    必须加锁 synchronized 才能保证单例,但加锁会影响效率,虽然getInstance() 的性能对应用程序不是很关键(该方法使用不太频繁)

实例

package 单例模式;

public class Singleton {
	private static Singleton singleobj;

	private Singleton() {
	}// 通过private限定,不能通过new建立对象,也不能继承(可以有继承操作,但是无意义)

	public static synchronized Singleton getinstance() {//synchronized 才能保证多线程单例
		if (singleobj != null)
			return singleobj;
		else {
			singleobj = new Singleton();
			return singleobj;
		}
	}

	public void dosomething() {
		System.out.println("一个女朋友就够了!!!");
	}
}
3. 饿汉式(线程安全,支持多线程)

对比:

  • 是否 Lazy 初始化:否
  • 是否多线程安全:是
  • 实现难度:易

描述:
这种方式比较常用,但容易产生垃圾对象。即以空间换时间的方式。
优点:
没有加锁,执行效率会提高。
缺点:
类加载时就初始化,浪费内存。
它基于 classloader 机制避免了多线程的同步问题, 虽然避免了多线程时调用getinstance时的线程问题,但是不能避免的是其他静态方法也能创建singleton对象。

实例

package 单例模式;

public class Singleton {
	private static Singleton singleobj = new Singleton();

	private Singleton() {
	}// 通过private限定,不能通过new建立对象,也不能继承(可以有继承操作,但是无意义)

	public static synchronized Singleton getinstance() {
		return singleobj;
	}

	public void dosomething() {
		System.out.println("一个女朋友就够了!!!");
	}

}

4.双检锁/双重校验锁 “DCL,即 double-checked locking”(线程安全,支持多线程)

对比:

  • 是否 Lazy 初始化:是
  • 是否多线程安全:是
  • 实现难度:较复杂

描述: 这种方式采用双锁机制,安全且在多线程情况下能保持高性能。
getInstance() 的性能对应用程序很关键。

实例

package 单例模式;

public class Singleton {
	 private volatile static  Singleton singleobj;

	private Singleton() {
	}// 通过private限定,不能通过new建立对象,也不能继承(可以有继承操作,但是无意义)

	public static synchronized Singleton getinstance() {
		 if (singleobj == null) {  
		        synchronized (Singleton.class) {  
		        if (singleobj == null) {  
		        	singleobj = new Singleton();  
		        }  
		        }  
		    }  
		    return singleobj;  
	}

	public void dosomething() {
		System.out.println("一个女朋友就够了!!!");
	}

}
 

其余方法: 登记式/静态内部类,枚举

应用给实例

配置文件,如果被系统的很多地方使用,那么不能总是去打开文件吧,那样很占用资源,所以我们可以使用单例模式,返回配置文件对象。

package 单例模式;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Properties;

public class Singel_Loder { //饿汉Hunger方式
	private static Singel_Loder singleobj = new Singel_Loder();

	private Singel_Loder() {
	}// 通过private限定,不能通过new建立对象,也不能继承(可以有继承操作,但是无意义)
	private static Properties props = new Properties(); 
    static { 
        try { 
            props.load(new FileInputStream("配置文件.properties")); 
        } catch (FileNotFoundException e) { 
            e.printStackTrace(); 
            System.exit(-1); 
        } catch (IOException e) {        
            System.exit(-1); 
        } 
    } 

	public static  Singel_Loder getinstance() {
		return singleobj;
	}

	public Properties getpro() {
		 
		return props;
	}
}

package 单例模式;

import java.util.Properties;

public class SingletonTest {

	public static void main(String[] args) {
		Singel_Loder sloder=Singel_Loder.getinstance();
		Properties pro=sloder.getpro();
		/**
		借助配置文件做一些事情
		
		 */
	}

}


写在最后:
Name:风骨散人,目前是一名双非在校大学生,预计考研,热爱编程,热爱技术,喜欢分享,知识无界,希望我的分享可以帮到你!名字的含义:我想有一天我能有能力随心所欲不逾矩,不总是向生活低头,有能力让家人拥有富足的生活而不是为了生计而到处奔波。“世人慌慌张张,不过是图碎银几两。偏偏这碎银几两,能解世间惆怅,可让父母安康,可护幼子成长 …”
文章主要内容:
Python,C++,C语言,JAVA,C#等语言的教程
ACM题解、模板、算法等,主要是数据结构,数学和图论
设计模式,数据库,计算机网络,操作系统,计算机组成原理
Python爬虫、深度学习、机器学习
计算机系408考研的所有专业课内容
目前还在更新中,先关注不迷路。微信公众号,cnblogs(博客园),CSDN同名“风骨散人”

如果有什么想看的,可以私信我,如果在能力范围内,我会发布相应的博文!
感谢大家的阅读!😘你的点赞、收藏、关注是对我最大的鼓励!

posted @ 2020-05-31 23:15  风骨散人  阅读(137)  评论(0编辑  收藏  举报