单例模式详解

目录

    1 单例模式概念

    2 单例模式的演示

           3 使用反射和序列化破解懒汉单例模式 以及如何防漏洞


概念

    单例模式,就是一个类只有一个实例对象,不管怎么做,都只有这个一个实例对象

    单例模式优点:只生成一个实例,减少了性能开销,当一个对象的生产需要比较多的资源时,如读取配置 产生其他依赖对象时,则可以通过应用启动时直接产生一个单例对象,然后永久驻留内存的方式来解决。   

    单例模式可以在系统设置全局的访问点,优化环共享资源访问时,例如可以设计一个单类负责所有数据表的映射处理

    常见的五种单例模式的实现方式 主要:1 饿汉模式(线程安全,调用效率高,但是不能延迟加载) 2懒汉模式(线程安全,调用效率不高,可以延时加载)

    其他 1 双重检测锁模式(由于jvm底层模型原因,会出问题,不推荐使用)2 静态内部类式(线程安全,调用效率高,但是,可以延迟加载,顾全了 饿汉模式和懒汉模式的有点,但又没有其缺点)3 枚举单例(线程安全,调用效率高,不能延时加载)


 

代码示例 一  懒汉模式   (线程安全,调用效率不高,能延迟加载) 

    

package singleton;

public class SingletonDemo1 {
	// 将构造方法私有化,防止外部访问,因此不可new出此类的对象
	private SingletonDemo1() {
		// TODO Auto-generated constructor stub
	}

	// 定义静态的本类对象
	private static SingletonDemo1 sing;

	// 写一个公开方法给外部调用 通过该方法获取实例对象 加上synchronized设为线程安全的
	public static synchronized SingletonDemo1 getInstance() {
		// 如果sing==null表明还没有被new new出一个sing返回 否则直接返回sing
		if (sing == null) {
			sing = new SingletonDemo1();
			return sing;
		} else {
			return sing;
		}

	}

}

  

package singleton;

public class SingletonTest {
	public static void main(String[] args) {
		SingletonDemo1 d1 = SingletonDemo1.getInstance();
		SingletonDemo1 d2 = SingletonDemo1.getInstance();
		SingletonDemo1 d3 = SingletonDemo1.getInstance();
		System.out.println(d1);
		System.out.println(d2);
		System.out.println(d3);

	}
}

  

对象内存地址一致


 

代码示例 二 饿汉模式   (线程安全,调用效率高,但是不能延迟加载) 

   

package singleton;

public class SingletonDemo02 {
	// 将构造方法私有化,防止外部访问,因此不可new出此类的对象
	private SingletonDemo02() {
		// TODO Auto-generated constructor stub
	}
	// 定义静态的本类对象 直接new出实例
	private static final SingletonDemo02 sing = new SingletonDemo02();
	// 写一个公开方法给外部调用 通过该方法获取实例对象 加上synchronized设为线程安全的
	public static synchronized SingletonDemo02 getInstance() {
		return sing;
	}

}

  

package singleton;

public class SingletonTest {
	public static void main(String[] args) {
		SingletonDemo02 d1 = SingletonDemo02.getInstance();
		SingletonDemo02 d2 = SingletonDemo02.getInstance();

		System.out.println(d1);
		System.out.println(d2);
	

	}
}

  

   内存地址一致 


示例三  内部静态类方式  (线程安全,调用效率高,但是,可以延迟加载,顾全了 饿汉模式和懒汉模式的有点,但又没有其缺点)

  

    

     

package singleton;

public class SingletonDemo03 {
	//定义一个静态内部类
	private static class SingStatic {
		private static final SingletonDemo03 sing = new SingletonDemo03();
	}
	//公开的方法,不需要加synchronized 因为他本身就是线程安全的
	public static  SingletonDemo03 getInstance() {
		return SingStatic.sing;
	}
}

  

package singleton;

public class SingletonTest {
	public static void main(String[] args) {
		SingletonDemo03 d1 = SingletonDemo03.getInstance();
		SingletonDemo03 d2 = SingletonDemo03.getInstance();

		System.out.println(d1);
		System.out.println(d2);
	

	}
}

  


示例四 枚举式单例模式 (线程安全,调用效率高,不难延时加载)

    

package singleton;

/**
 * 枚举方式可以避免 反射和反序列化的漏洞,调用效率也比较高,很遗憾没有延迟加载
 * 
 * @author re
 *
 */
public enum SingletonDemo04 {
	// 这个枚举元素本身就是单例对象
	instance;
	// 可以写自己想要的操作
	public void printenum() {
		System.out.println("这是枚举示例");
	}

}

  

package singleton;

public class SingletonTest {
	public static void main(String[] args) {
		SingletonDemo04 d1 = SingletonDemo04.instance;
		SingletonDemo04 d2 = SingletonDemo04.instance;

		System.out.println(d1 == d2);

	}
}

  

比较为true  是同一个对象


破解单例模式 和防漏洞 (枚举无法破解,因为它是基于JVM底层实现的)

   一懒汉模式为例,使用反序列化和反射进行破解   

   1 使用反射破解

package singleton;

public class SingletonDemo1 {
	// 将构造方法私有化,防止外部访问,因此不可new出此类的对象
	private SingletonDemo1() {
		// TODO Auto-generated constructor stub
	}

	// 定义静态的本类对象
	private static SingletonDemo1 sing;

	// 写一个公开方法给外部调用 通过该方法获取实例对象 加上synchronized设为线程安全的
	public static synchronized SingletonDemo1 getInstance() {
		// 如果sing==null表明还没有被new new出一个sing返回 否则直接返回sing
		if (sing == null) {
			sing = new SingletonDemo1();
			return sing;
		} else {
			return sing;
		}

	}

}

  

package singleton;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class SingletonTest {
	public static void main(String[] args) throws NoSuchMethodException, SecurityException, InstantiationException,
			IllegalAccessException, IllegalArgumentException, InvocationTargetException {
		// 获取class对象
		Class clazz = SingletonDemo1.class;
		// 获取构造器
		Constructor c1 = clazz.getDeclaredConstructor(null);
		c1.setAccessible(true);// 跳过访问检查 如果不设为true 则无法访问私有属性,
		// 因此可以访问私有的构造方法,从而创建对象,利用这个漏洞可以破解单例模式
		SingletonDemo1 d1 = (SingletonDemo1) c1.newInstance(null);
		SingletonDemo1 d2 = (SingletonDemo1) c1.newInstance(null);
		System.out.println(d1);
		System.out.println(d2);
	}
}

  

输出的  对象地址不一样   将SinglentoDemo1中的私有构造方法改为  可防止漏洞,不过我的这个有些问题,加上之后当sing!=null new对象时,并不能抛出异常。

private SingletonDemo1() {
		// TODO Auto-generated constructor stub
		if(sing!=null){
			//如果有已有sing实例,表示被new过,再创建对象时抛出异常
			throw new RuntimeException();
		}
	}

反序列化防止漏洞方法

  在单例类中加入  下面这个方法  方法名称和返回值类型不要改动

// 在反序列化时,直接调用这个方法,返回指定的对象,无需再新建一个对象
	private Object readResolve() {
		return sing;
	}

 枚举式单例模式和静态内部类单例模式如何选择

  

原文地址:https://www.cnblogs.com/sanduweiliangxtx/p/6144712.html