一:什么是单例设计模式
单例例模式便是创建型设计模式的一种,它确保某一个类在系统中只有一个实例,并自行实例化,同时向外部提供获取这个唯一实例的接口。 单例设计模式主要有以下三个特点:
1、只能有一个实例。
2、必须自己创建自己的唯一实例。
3、必须给所有其他对象提供这一实例。
二:单例设计模式种类
饿汉设计模式:类加载就会导致改单例实例对象被创建
饿汉设计模式分为两种:
1.静态成员变量的方式
2.静态代码块方式
懒汉设计模式:类加载不会创建对象,首次使用才会创建对象
懒汉设计模式分为六种:
1.懒汉设计模式 (线程不安全)
2.懒汉设计模式 (线程安全,效率低)
3.懒汉设计模式 (双重检查锁模式)volatile 关键字
4.懒汉设计模式(静态内部类模式)
5.懒汉设计模式(序列化破坏单例)readResolve()方法
6.懒汉设计模式(反射破坏单例)
7.懒汉设计模式(枚举)
三:饿汉设计模式
1.静态成员变量的方式:
/**
* 饿汉设计模式 (静态成员变量的方式)
*/
public class ThehungryDemo {
//私有构造方法,外界不能创建对象
private ThehungryDemo() {
}
// 静态变量进行创建声明
private static ThehungryDemo thehungryDemo=new ThehungryDemo();
//通过公共方法进行创建返回,
public static ThehungryDemo getInstance(){
return thehungryDemo;
}
}
2.静态代码块方式:
/**
* 饿汉设计模式 (静态代码块方式)
*/
public class ThehungryDemo2 {
//私有构造方法,外界不能创建对象
private ThehungryDemo2(){
}
// 静态变量进行创建声明
private static ThehungryDemo2 thehungryDemo;
static {
thehungryDemo=new ThehungryDemo2();
}
//通过公共方法进行创建返回,
public static ThehungryDemo2 getInstance(){
return thehungryDemo;
}
}
饿汉设计模式总结:
缺点: 以空间换时间,在实例化时就创建,灵活性不高,空间使用率不高。
优点:在类加载过程中便会创建。由此带来的好处是Java的类加载机制本身为我们保证了实例化过程的线程安全性。
四:懒汉设计模式
1.懒汉设计模式 (线程不安全)
多线程情况下会出现线程不安全
/**
* 懒汉设计模式 (线程不安全)
*/
public class IdlerDemo {
//私有无参构造方法
private IdlerDemo() {
}
// 声明静态的对象
private static IdlerDemo idlerDemo;
// 静态的无参获取对象
public static IdlerDemo getInstance(){
// 此处会出现,多线程争抢时间片的情况,导致线程不安全
if (idlerDemo == null) {
idlerDemo = new IdlerDemo();
}
return idlerDemo;
}
}
2.懒汉设计模式 (线程安全,效率低)
读写是互斥,但是读和读不互斥
/**
* 懒汉设计模式 (线程安全,效率低)
* synchronized 关键字
*/
public class IdlerDemo {
//私有无参构造方法
private IdlerDemo() {
}
// 声明静态的对象
private static IdlerDemo idlerDemo;
// 静态的无参获取对象
// 加入 synchronized 关键字后,保证了线程安全。读写是互斥,但是读和读不互斥
public static synchronized IdlerDemo getInstance(){
if (idlerDemo == null) {
idlerDemo = new IdlerDemo();
}
return idlerDemo;
}
}
3.懒汉设计模式 (双重检查锁模式) volatile 关键字重点
volatile 关键字三大特性
1.原子性:一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。
2.可见性:指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。
3.有序性:即程序执行的顺序按照代码的先后顺序执行。
/**
* 懒汉设计模式 (双重检查锁模式)
*/
public class IdlerDemo2 {
//声明私有构造方法
private IdlerDemo2() {
}
// //声明静态的对象
// 不使用volatile关键字 ,在多线程时会报空指针错误 。因为JVM在运行时无法保证有序
// private static IdlerDemo2 idlerDemo2;
//声明静态的对象
// volatile 关键字可以保证我们JVM在运行的时候有序进行,保证线程安全
private static volatile IdlerDemo2 idlerDemo2;
// 声明公共get()方法
public static IdlerDemo2 getInstance(){
// 在if判断当中加入了synchronized 锁,保证了读读不互斥,提高了效率
if(idlerDemo2 == null){
synchronized (IdlerDemo2.class){
idlerDemo2=new IdlerDemo2();
}
}
return idlerDemo2;
}
}
4.懒汉设计模式(静态内部类模式)
JVM在加载外部类的时候,不会加载静态内部类
/**
* 懒汉设计模式(静态内部类模式)
* JVM在加载外部类的时候,不会加载静态内部类
*/
public class IdlerDemo3 {
//声明私有构造方法
private IdlerDemo3(){
}
//声明静态内部类 ,并给与一个静态常量创建对象。
private static class IdlerDemoHolder {
private static final IdlerDemo3 IDLERDEMO= new IdlerDemo3();
}
// 获取静态内部类常量创建对象
public static IdlerDemo3 getInstance(){
return IdlerDemoHolder.IDLERDEMO;
}
}
5.懒汉设计模式(序列化破坏单例)readResolve()方法
通过输入流,输出流破坏单例
/**
* 输入流,输出流破坏单例模式
*/
public class Test {
public static void main(String[] args)throws Exception {
writeObjectOutputStream();
IdlerDemo3 idlerDemo3 = writeObjectInputStream();
IdlerDemo3 idlerDemo31 = writeObjectInputStream();
System.out.println(idlerDemo3==idlerDemo31);
}
/**
* 输入流,输入对象
* @throws IOException
*/
public static void writeObjectOutputStream() throws IOException {
IdlerDemo3 instance = IdlerDemo3.getInstance();
ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("F:\\a.txt"));
oos.writeObject(instance);
oos.close();
}
/**
* 输出流获取对象
* @return
* @throws Exception
*/
public static IdlerDemo3 writeObjectInputStream()throws Exception{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("F:\\a.txt"));
IdlerDemo3 idlerDemo3=(IdlerDemo3) ois.readObject();
System.out.println(idlerDemo3);
return idlerDemo3;
}
}
解决办法:实现 readResolve()方法
public class IdlerDemo3 implements Serializable {
private IdlerDemo3(){
}
private static class IdlerDemoHolder {
private static final IdlerDemo3 IDLERDEMO= new IdlerDemo3();
}
public static IdlerDemo3 getInstance(){
return IdlerDemoHolder.IDLERDEMO;
}
/**
* 查看源码发现,实现序列化接口Serializable之后
* 需要实现readResolve()方法, 序列化底层方法会判断序列化对象有没实现有这个方法,
* 如果没有就return新的对象
* @return
*/
public Object readResolve() {
return IdlerDemoHolder.IDLERDEMO;
}
}
6.懒汉设计模式(反射破坏单例)
问题:反射可以通过突破你的封装,获取你的无参构造方法创建对象。
解决办法:在无参构造方法中加入锁synchronized 保证线程安全,并且加入判断只在第一次突破封装时创建。
public class IdlerDemo3 implements Serializable {
//声明一个全局静态变量,初始为false
private static Boolean finale=false;
private IdlerDemo3(){
// 加上锁, 第一次让他进行创建,第二次就抛异常。防止反射进行获取对象
synchronized (IdlerDemo3.class){
if (finale){
throw new RuntimeException("不能创建多个对象");
}
}
finale =true;
}
private static class IdlerDemoHolder {
private static final IdlerDemo3 IDLERDEMO= new IdlerDemo3();
}
public static IdlerDemo3 getInstance(){
return IdlerDemoHolder.IDLERDEMO;
}
}
7.懒汉设计模式(枚举)
因为枚举类型是线程安全的,并且只会装载一次。所以枚举设计模式是线程安全的,并且是唯一个不会被破坏的单例实现模式。
/**
* 懒汉模式(枚举),但是还是饿汉式模式(消耗内存)
*/
public enum IdlerDemo4 {
IDLER_DEMO_4;
}
懒汉设计模式总结
缺点:不适用于变化的对象,如果同一类型的对象总是要在不同的用例场景发生变化,单例设计模式就不能保存彼此的状态,导致数据错误。
优点:由于在系统内存中只存在一个对象,因此可以 节约系统资源,当 需要频繁创建和销毁的对象时单例模式无疑可以提高系统的性能。并且可以避免共享资源的多重占用。