
常见设计模式-创建型模式-单例模式本文介绍了单例设计模式其他常见设计模式在本系列其他文章有兴趣可了解文章目录常见设计模式-创建型模式-单例模式创建型模式-单例模式**饿汉式****懒汉式****双重检查锁方式****volatile关键字****静态内部类方式****枚举方式****序列化破坏单例模式****反射破坏单例模式****序列化破坏单例模式解决方案****反射破坏单例模式解决方法**总结创建型模式-单例模式这种模式涉及到一个单一的类该类负责创建自己的对象同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式可以直接访问不需要实例化该类的对象。单例只有一个实例对象构造方法私有类成员变量static修饰私有单例设计模式分类两种饿汉式类加载就会导致该单实例对象被创建懒汉式类加载不会导致该单实例对象被创建而是首次使用该对象时才会创建饿汉式//饿汉式静态变量方式publicclassSingleton{privateSingleton(){}privatestaticSingletoninstancenewSingleton();publicstaticSingletongetInstance(){returninstance;}}//饿汉式静态代码块方式publicclassSingleton{privateSingleton(){}privatestaticSingletoninstance;static{instancenewSingleton();}publicstaticSingletongetInstance(){returninstance;}}饿汉式不管有没有用到都会创建如果对象很大容易造成内存浪费懒汉式//懒汉式线程不安全publicclassSingleton{privateSingleton(){}privatestaticSingletoninstance;publicstaticSingletongetInstance(){if(instancenull){instancenewSingleton();}returninstance;}}//懒汉式线程安全get方法加synchronizedpublicclassSingleton{privateSingleton(){}privatestaticSingletoninstance;publicstaticsynchronizedSingletongetInstance(){if(instancenull){instancenewSingleton();}returninstance;}}单例模式大部分操作是读操作即一次创建后续直接拿读因此这种加锁方式会造成性能浪费加锁是为了防止创建多个实例对象保证了单例只有一个实例对象但创建好对象后大部分操作是只需要拿读就行读操作本身不存在线程安全问题这种加锁方式导致一个线程就算只是拿数据也必须等拿到锁的线程拿完数据释放锁后才能进行操作为解决这种性能浪费引出了双重检查锁方式双重检查锁方式//双重检查锁方式publicclassSingleton{privateSingleton(){}privatestaticSingletoninstance;publicstaticSingletongetInstance(){if(instancenull){synchronized(Singleton.class){if(instancenull){instancenewSingleton();}}}returninstance;}}双重检查锁模式是一种非常好的单例实现模式解决了单例、性能、线程安全问题上面的双重检测锁模式看上去完美无缺其实是存在问题在多线程的情况下可能会出现空指针问题出现问题的原因是JVM在实例化对象的时候会进行优化和指令重排序操作。volatile关键字要解决双重检查锁模式带来的空指针异常的问题只需要使用volatile关键字volatile关键字可以保证可见性和有序性。publicclassSingleton{privateSingleton(){}privatestaticvolatileSingletoninstance;publicstaticSingletongetInstance(){if(instancenull){synchronized(Singleton.class){if(instancenull){instancenewSingleton();}}}returninstance;}}静态内部类方式静态内部类单例模式中实例由内部类创建由于 JVM 在加载外部类的过程中是不会加载静态内部类的涉及到JVM类加载顺序只有内部类的属性/方法被调用时才会被加载并初始化其静态属性。静态属性由于被 static 修饰保证只被实例化一次并且严格保证实例化顺序。final修饰的成员变量必须在声明时或初始化时或代码块中赋值//静态内部类方式publicclassSingleton{privateSingleton(){}//静态内部类privatestaticclassSingletonHolder{privatestaticfinalSingletonINSTANCEnewSingleton();}// 对外提供静态方法获取该对象publicstaticSingletongetInstance(){returnSingletonHolder.INSTANCE;}}枚举方式枚举类实现单例模式是极力推荐的单例实现模式因为枚举类型是线程安全的并且只会装载一次设计者充分的利用了枚举的这个特性来实现单例模式枚举的写法非常简单而且枚举类型是所用单例实现中唯一一种不会被破坏的单例实现模式。枚举方式属于饿汉式方式。//枚举方式publicenumSingleton{INSTANCE;}序列化破坏单例模式先让Singleton类实现Serializable接口//静态内部类方式publicclassSingletonimplementsSerializable{privateSingleton(){}//静态内部类privatestaticclassSingletonHolder{privatestaticfinalSingletonINSTANCEnewSingleton();}// 对外提供静态方法获取该对象publicstaticSingletongetInstance(){returnSingletonHolder.INSTANCE;}}写一次数据后读两次发现两次对象不一样publicclassClient{publicstaticvoidmain(String[]args)throwsException{//writeObject2File();readObjectFromFile();readObjectFromFile();}//从文件读取数据对象publicstaticvoidreadObjectFromFile()throwsException{ObjectInputStreamoisnewObjectInputStream(newFileInputStream(C:\\Users\\lenovo\\Desktop\\a.txt));Singletoninstance(Singleton)ois.readObject();System.out.println(instance);ois.close();}// 向文件中写数据对象publicstaticvoidwriteObject2File()throwsException{SingletoninstanceSingleton.getInstance();ObjectOutputStreamoosnewObjectOutputStream(newFileOutputStream(C:\\Users\\lenovo\\Desktop\\a.txt));oos.writeObject(instance);oos.close();}}com.tylg.demo5.Singleton4f2410accom.tylg.demo5.Singleton722c41f4反射破坏单例模式publicclassClient{publicstaticvoidmain(String[]args)throwsException{ClassclazzSingleton.class;Constructorconsclazz.getDeclaredConstructor();// 取消访问检查cons.setAccessible(true);Singletons1(Singleton)cons.newInstance();Singletons2(Singleton)cons.newInstance();System.out.println(s1s2);}}false序列化破坏单例模式解决方案在单例模式中使用readResolve方法当进行反序列化时写会自动执行这个方法//静态内部类方式publicclassSingletonimplementsSerializable{privateSingleton(){}//静态内部类privatestaticclassSingletonHolder{privatestaticfinalSingletonINSTANCEnewSingleton();}// 对外提供静态方法获取该对象publicstaticSingletongetInstance(){returnSingletonHolder.INSTANCE;}// 反序列化时返回新的对象protectedObjectreadResolve(){returngetInstance();}}反射破坏单例模式解决方法publicclassSingletonimplementsSerializable{privateSingleton(){if(SingletonHolder.INSTANCE!null){thrownewRuntimeException(禁止创建多个对象);}}//静态内部类privatestaticclassSingletonHolder{privatestaticfinalSingletonINSTANCEnewSingleton();}// 对外提供静态方法获取该对象publicstaticSingletongetInstance(){returnSingletonHolder.INSTANCE;}// 反序列化时返回新的对象protectedObjectreadResolve(){returngetInstance();}}反射通过使用私有化构造器创建新对象那我们就在这进行防御总结以上就是本期要讲的内容本文介绍了单例设计模式其他常见设计模式在本系列其他文章有兴趣可了解