创建型模式-单例模式
1. 单例模式的定义与特点
单例(Singleton)模式的定义:指一个类只有一个实例,且该类能自行创建这个实例的一种模式。
单例模式有 3 个特点:
- 单例类只有一个实例对象;
- 该单例对象必须由单例类自行创建;
- 单例类对外提供一个访问该单例的全局访问点。
2. 单例模式的优点和缺点
单例模式的优点:
- 单例模式可以保证内存里只有一个实例,减少了内存的开销。
- 可以避免对资源的多重占用。
- 单例模式设置全局访问点,可以优化和共享资源的访问。
单例模式的缺点:
- 单例模式一般没有接口,扩展困难。如果要扩展,则除了修改原来的代码,没有第二种途径,违背开闭原则。
- 在并发测试中,单例模式不利于代码调试。在调试过程中,如果单例中的代码没有执行完,也不能模拟生成一个新的对象。
- 单例模式的功能代码通常写在一个类中,如果功能设计不合理,则很容易违背单一职责原则。
3. 单例模式的应用场景
- 需要频繁创建的一些类,使用单例可以降低系统的内存压力,减少 GC。
- 某类只要求生成一个对象的时候,如一个班中的班长、每个人的身份证号等。
- 某些类创建实例时占用资源较多,或实例化耗时较长,且经常使用。
- 各种连接池等。
- spring默认Bean的模式
4. 单例模式的结构
- 必须定义一个静态私有实例,并向外提供一个静态的公有函数用于创建或获取该静态私有实例
5.单例模式的结构实现
- 懒汉式单例(获取实例为空才创建实例)
public class Singleton {
public static void main(String[] args) {
LazySingleton instance1 = LazySingleton.getInstance();
LazySingleton instance2 = LazySingleton.getInstance();
System.out.println(instance1 == instance2);
}
}
/**
1. 懒汉式单例
*/
class LazySingleton {
/**
* volatile 保证了不同线程对这个变量进行操作时的可见性
* 禁止进行指令重排序(代码执行顺序)
*/
private static volatile LazySingleton lazySingleton = null;
/**
* 构造器私有化
*/
private LazySingleton() {
}
/**
* 双重验证 防止多线程下获取为空
*
* @return
*/
public static LazySingleton getInstance() {
if (lazySingleton == null) {
synchronized (LazySingleton.class) {
if (lazySingleton == null) {
lazySingleton = new LazySingleton();
}
}
}
return lazySingleton;
}
}
- 饿汉式单例(实例在类加载时创建)
public class Singleton {
public static void main(String[] args) {
HungrySingleton instance1 = HungrySingleton.getInstance();
HungrySingleton instance2 = HungrySingleton.getInstance();
System.out.println(instance1 == instance2);
}
}
/**
* 饿汉式单例
* 实例在类加载时创建
* 保证在调用 getInstance 方法之前单例已经存在了。
*/
class HungrySingleton {
private HungrySingleton() {
}
private static final HungrySingleton hungrySingleton = new HungrySingleton();
public static HungrySingleton getInstance() {
return hungrySingleton;
}
}
6.单例模式的扩展
单例模式可扩展为有限的多例(Multitcm)模式(比如连接池)
/**
* 有限多例模式(应用场景 连接池)
*/
class Multitcm {
private static int maxNum = 8;
private static int currNum = 0;
private static ArrayList<String> multitcmList = new ArrayList<>(maxNum);
private static ArrayList<Multitcm> connArrayList = new ArrayList<>(maxNum);
private Multitcm() {
}
private Multitcm(String info) {
multitcmList.add(info);
}
static {
for (int i = 0; i < maxNum; i++) {
connArrayList.add(new Multitcm(i + "号连接"));
}
}
public static Multitcm getInstance() {
Random random = new Random();
currNum = random.nextInt(maxNum);
return connArrayList.get(currNum);
}
public void connectionInfo() {
System.out.println(multitcmList.get(currNum));
}
}
7.破坏单例模式的方式
- 克隆-浅克隆(实现Cloneable接口重写Object的clone方法)
public class Singleton {
public static void main(String[] args) throws CloneNotSupportedException {
LazySingleton instance = LazySingleton.getInstance();
LazySingleton clone = (LazySingleton) instance.clone();
System.out.println(instance == clone);
}
}
/**
1. 懒汉式单例
*/
class LazySingleton implements Cloneable {
/**
* volatile 保证了不同线程对这个变量进行操作时的可见性
* 禁止进行指令重排序(代码执行顺序)
*/
private static volatile LazySingleton lazySingleton = null;
/**
* 构造器私有化
*/
private LazySingleton() {
}
/**
* 双重验证 防止多线程下获取为空
*
* @return
*/
public static LazySingleton getInstance() {
if (lazySingleton == null) {
synchronized (LazySingleton.class) {
if (lazySingleton == null) {
lazySingleton = new LazySingleton();
}
}
}
return lazySingleton;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
- 克隆-深克隆(实现Serializable 接口,先序列化在反序列化)
public class Singleton {
public static void main(String[] args) throws Exception {
LazySingleton s1=LazySingleton.getInstance();
//序列化
ByteArrayOutputStream bos=new ByteArrayOutputStream();
ObjectOutputStream oos=new ObjectOutputStream(bos);
oos.writeObject(s1);
//反序列化
ByteArrayInputStream bis=new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois=new ObjectInputStream(bis);
LazySingleton s2= (LazySingleton) ois.readObject();
System.out.println(s1);
System.out.println(s2);
}
}
/**
1. 懒汉式单例
*/
class LazySingleton implements Serializable {
private static final Long serialVersionUID = 1L;
/**
* volatile 保证了不同线程对这个变量进行操作时的可见性
* 禁止进行指令重排序(代码执行顺序)
*/
private static volatile LazySingleton lazySingleton = null;
/**
* 构造器私有化
*/
private LazySingleton() {
}
/**
* 双重验证 防止多线程下获取为空
*
* @return
*/
public static LazySingleton getInstance() {
if (lazySingleton == null) {
synchronized (LazySingleton.class) {
if (lazySingleton == null) {
lazySingleton = new LazySingleton();
}
}
}
return lazySingleton;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
- 反射
public static void main(String[] args) throws CloneNotSupportedException, Exception {
LazySingleton instance = LazySingleton.getInstance();
Class<? extends LazySingleton> aClass = instance.getClass();
Constructor<? extends LazySingleton> declaredConstructor = aClass.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
LazySingleton lazySingleton = declaredConstructor.newInstance();
System.out.println(instance == lazySingleton);
}
- (反射)第一种解决方法(在构造器判断实例是否为空,不等于null直接抛异常。但是反射可以修改私有变量的值,跳过判断,前提知道参数名称)
/**
* 构造器私有化
*/
private LazySingleton() {
if(lazySingleton!=null){
throw new RuntimeException("老子是单例,你别想破坏!");
}
System.out.println("private Singleton()");
}
- (反射)第二种解决方法(使用内部类的方式)
class HungrySingleton {
private HungrySingleton() {
if(LazyHolder.LAZY != null){
throw new RuntimeException("不允许创建多个实例");
}
}
public static final HungrySingleton getInstance(){
//在返回结果以前,一定会先加载内部类
return LazyHolder.LAZY;
}
//默认不加载
private static class LazyHolder{
private static final HungrySingleton LAZY = new HungrySingleton();
}
}
8.总结
设计模式不一定必须遵守设计原则,按需设计。