单例设计模式:看名字就可以大体的理解单例设计模式是什么了,它是用来创建独一无二的对象的,是能有一个实例的对象的模式.(我的理解是在整个内存中只能存在一个该实例对象)
单例模式在常用的有种实现方式一.懒汉式 二.饿汉式他们的特点就是构造私有并提供获得该实例的方法从而获得该实例
一,饿汉式
饿汉式可以理解为非常的迫切在你没有用该对象的时候就已经实例化了具体代码如下.
/**
* <p> 饿汉式的实现 </p>
*
* @author Alemand
* @since 2018/1/10
*/
public class Singleton {
/**
*使用static修饰直接初始化
*/
private static Singleton singleton = new Singleton();
/**
*单例设计模式的特点构造私有化
*/
private Singleton(){};
/**
*提供访问的接口返回该实例
*
* @return 该类的实例
*/
public static Singleton getSingleton(){
return singleton;
}
}
这就是饿汉式的代码实现较为简单但在开发中是不会用这种的方式通常用的是懒汉式.
二.懒汉式可以理解为是不迫切的很懒只有在用的时候才会给你实例.代码如下
/**
* <p> 懒汉式的实现 </p>
*
* @author Alemand
* @since 2018/1/10
*/
public class Singleton {
/**
* 使用static修饰没有初始化
*/
private static Singleton singleton;
/**
* 单例设计模式的特点构造私有化
*/
private Singleton() {};
/**
* 提供访问的接口返回该实例
*
* @return 该类的实例
*/
public static Singleton getSingleton() {
/**
*实例为空的时候才创建该实例
*/
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
这样在单线程的情况下是没有问题的但是如果在多线程的情况下就会有问题,可能会产生多个实例对象,这样的话这个单例是错误的单例了.我们可以用synchronized关键字来实现代码如下
/**
* <p> 懒汉式的实现 </p>
*
* @author Alemand
* @since 2018/1/10
*/
public class Singleton {
/**
* 使用static修饰没有初始化
*/
private static Singleton singleton;
/**
* 单例设计模式的特点构造私有化
*/
private Singleton() {};
/**
* 提供访问的接口返回该实例
*
* @return 该类的实例
*/
public synchronized static Singleton getSingleton() {
/**
*实例为空的时候才创建该实例
*/
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
这样的还是有问题每一次线程访问的时候都会经过synchronized这样的话效率会降低.还有更好的的方法那就是双重检查加锁代码如下
/**
* <p> 懒汉式的实现最终版 </p>
*
* @author Alemand
* @since 2018/1/10
*/
public class Singleton {
/**
* 使用static修饰没有初始化并使用volatile修饰是变量可见
*/
private volatile static Singleton singleton;
/**
* 单例设计模式的特点构造私有化
*/
private Singleton() {};
/**
* 提供访问的接口返回该实例
*
* @return 该类的实例
*/
public static Singleton getSingleton() {
//第一次判断为空来来确定是否进synchronized
if (singleton == null){
synchronized (Singleton.class){
//第二次的判断来确定是否创建
if (singleton == null){
//创建实例对象
singleton = new Singleton();
}
}
}
return singleton;
}
}
这样做的话只有该对象为空的时候才去经过synchronized这样的话就提高了效率同时使用了volatile关键字保证可实例的可见性这样做的话才是一个比较好的单例实现
单例模式的扩展
使用CAS来实现
package com.alemand;
import java.util.concurrent.atomic.AtomicReference;
/**
* <p>
* CAS实现单例模式
* </p>
*
* @author Alemand
* @since 2018/10/31
*/
public class Singleton {
/**
* 使用CAS的实现atomic
*/
private static final AtomicReference<Singleton> atomicReference = new AtomicReference<Singleton>();
/**
* 单例的引用
*/
private static volatile Singleton singleton;
/**
* 构造方法私有
*/
private Singleton() {
}
/**
* 获取单例的方法
*
* @return 返回实例对象
*/
public static Singleton getInstance() {
if (atomicReference.compareAndSet(null, new Singleton())) {
return singleton = atomicReference.get();
} else {
return singleton = atomicReference.get();
}
}
}
静态内部类实现单例
package com.alemand;
/**
* <p>
* CAS实现单例模式
* </p>
*
* @author Alemand
* @since 2018/10/31
*/
public class Singleton {
/**
* 构造方法私有
*/
private Singleton() {
}
/**
*内部类
*/
private static class SingletonHelp {
public static final Singleton singleton = new Singleton();
}
/**
* 获取类的实例
*
* @return
*/
public static Singleton getInstance() {
return SingletonHelp.singleton;
}
}
单例的优点:
- 提供了对唯一实例的受控访问。因为单例类封装了它的唯一实例,所以它可以严格控制客户怎样以及何时访问它,并为设计及开发团队提供了共享的概念。
- 由于在系统内存中只存在一个对象,因此可以节约系统资源,对于一些需要频繁创建和销毁的对象,单例模式无疑可以提高系统的性能。
- 允许可变数目的实例。我们可以基于单例模式进行扩展,使用与单例控制相似的方法来获得指定个数的对象实例。
单例的缺点:
- 由于单例模式中没有抽象层,因此单例类的扩展有很大的困难。
- 单例类的职责过重,在一定程度上违背了“单一职责原则”。因为单例类既充当了工厂角色,提供了工厂方法,同时又充当了产品角色,包含一些业务方法,将产品的创建和产品的本身的功能融合到一起。
- 滥用单例将带来一些负面问题,如为了节省资源将数据库连接池对象设计为单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出.
应用场景:
- 系统只需要一个实例对象,如系统要求提供一个唯一的序列号生成器,或者需要考虑资源消耗太大而只允许创建一个对象。
- 客户调用类的单个实例只允许使用一个公共访问点,除了该公共访问点,不能通过其他途径访问该实例。
- 在一个系统中要求一个类只有一个实例时才应当使用单例模式。反过来,如果一个类可以有几个实例共存,就需要对单例模式进行改进,使之成为多例模式
注意:
1.单例设计模式的实现方式已经写完了,在这里你可能还会有一些疑问垃圾回收会不会回收单例的实例呢,答案是不会如果是1.2以前的话会有这个问题,但是现在应该没人在用1.2以前的了.
2.单例的类是不可以被继承因为他的构造方法是有了,不要在往下问为什么了,在问的话你就要去看继承了.继承没学好
3.如果确实想要实例化第二个单例可以使用暴力反射.可以将该类实例化了