单例模式3--单例模式

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/sinat_16658263/article/details/89293015

什么是单例

 保证一个类只有一个实例,并且提供一个访问该全局访问点

单例优缺点

优点: 

    1.在单例模式中,活动的单例只有一个实例,对单例类的所有实例化得到的都是相同的一个实例。这样就 防止其它对象对自己的实例化,确保所有的对象都访问一个实例 

    2.单例模式具有一定的伸缩性,类自己来控制实例化进程,类就在改变实例化进程上有相应的伸缩性。

    3.提供了对唯一实例的受控访问。

    4.由于在系统内存中只存在一个对象,因此可以 节约系统资源,当 需要频繁创建和销毁的对象时单例模式无疑可以提高系统的性能。 

    5.允许可变数目的实例。

    6.避免对共享资源的多重占用。

缺点: 

    1.不适用于变化的对象,如果同一类型的对象总是要在不同的用例场景发生变化,单例就会引起数据的错误,不能保存彼此的状态。

    2.由于单利模式中没有抽象层,因此单例类的扩展有很大的困难。

    3.单例类的职责过重,在一定程度上违背了“单一职责原则”。

    4.滥用单例将带来一些负面问题,如为了节省资源将数据库连接池对象设计为的单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出;如果实例化的对象长时间不被利用,系统会认为是垃圾而被回收,这将导致对象状态的丢失。

单例创建方式

  1. 饿汉式:类初始化时,会立即加载该对象,线程天生安全,调用效率高。
  2. 懒汉式: 类初始化时,不会初始化该对象,真正需要使用的时候才会创建该对象,具备懒加载功能。
  3. 静态内部方式:结合了懒汉式和饿汉式各自的优点,真正需要对象的时候才会加载,加载类是线程安全的。
  4. 枚举单例: 使用枚举实现单例模式 优点:实现简单、调用效率高,枚举本身就是单例,由jvm从根本上提供保障!避免通过反射和反序列化的漏洞, 缺点没有延迟加载。
  5. 双重检测锁方式 (因为JVM本质重排序的原因,可能会初始化多次,不推荐使用)

 

饿汉式

//饿汉式

public class SingletonDemo01 {

     // 类初始化时,会立即加载该对象,线程天生安全,调用效率高

     private static SingletonDemo01 singletonDemo01 = newSingletonDemo01();

 

     private SingletonDemo01() {

           System.out.println("SingletonDemo01初始化");

     }

 

     public static SingletonDemo01 getInstance() {

           System.out.println("getInstance");

           return singletonDemo01;

     }

 

     public static void main(String[] args) {

           SingletonDemo01 s1 = SingletonDemo01.getInstance();

           SingletonDemo01 s2 = SingletonDemo01.getInstance();

           System.out.println(s1 == s2);

     }

 

}

 

 

 

懒汉式

//懒汉式

public class SingletonDemo02 {

 

     //类初始化时,不会初始化该对象,真正需要使用的时候才会创建该对象。

     private staticSingletonDemo02 singletonDemo02;

 

     private SingletonDemo02() {

   

     }

 

     public synchronized staticSingletonDemo02 getInstance() {

           if (singletonDemo02 == null) {

                singletonDemo02 = new SingletonDemo02();

           }

           return singletonDemo02;

     }

 

     public static voidmain(String[] args) {

           SingletonDemo02 s1 = SingletonDemo02.getInstance();

           SingletonDemo02 s2 = SingletonDemo02.getInstance();

           System.out.println(s1 == s2);

     }

 

}

 

 

 

静态内部类

// 静态内部类方式

public class SingletonDemo03 {

     private SingletonDemo03() {

           System.out.println("初始化..");

     }

 

     public static class SingletonClassInstance {

           private static final SingletonDemo03 singletonDemo03 = newSingletonDemo03();

     }

 

     // 方法没有同步

     public static SingletonDemo03 getInstance() {

           System.out.println("getInstance");

           returnSingletonClassInstance.singletonDemo03;

     }

 

     public static void main(String[] args) {

           SingletonDemo03 s1 = SingletonDemo03.getInstance();

           SingletonDemo03 s2 = SingletonDemo03.getInstance();

           System.out.println(s1 == s2);

     }

}

 优势:兼顾了懒汉模式的内存优化(使用时才初始化)以及饿汉模式的安全性(不会被反射入侵)。

 

 劣势:需要两个类去做到这一点,虽然不会创建静态内部类的对象,但是其Class 对象还是会被创建,而且是属于永久带的对象。

枚举方式

什么是枚举

枚举本身是单例的,一般用于项目中定义常量。

enum UserEnum {

     HTTP_200(200, "请求成功"),HTTP_500(500,"请求失败");

     private Integer code;

     private String name;

 

     UserEnum(Integer code, String name) {

           this.code = code;

           this.name = name;

     }

 

     public Integer getCode() {

           return code;

     }

 

     public void setCode(Integer code) {

           this.code = code;

     }

 

     public String getName() {

           return name;

     }

 

     public void setName(String name) {

           this.name = name;

     }

 

}

 

public class TestEnum {

 

     public static void main(String[] args) {

           System.out.println(UserEnum.HTTP_500.getCode());

     }

 

}

 

 

 

/使用枚举实现单例模式 优点:实现简单、枚举本身就是单例,由jvm从根本上提供保障!避免通过反射和反序列化的漏洞 缺点没有延迟加载

public class User {

     public static User getInstance() {

           returnSingletonDemo04.INSTANCE.getInstance();

     }

 

     private static enum SingletonDemo04 {

           INSTANCE;

           // 枚举元素为单例

           private User user;

 

           private SingletonDemo04() {

                System.out.println("SingletonDemo04");

                user = new User();

           }

 

           public User getInstance() {

                return user;

           }

     }

 

     public static void main(String[] args) {

           User u1 = User.getInstance();

           User u2 = User.getInstance();

           System.out.println(u1 == u2);

     }

}

 

 

 

 

双重检测锁

public classSingletonDemo04 {

     privateSingletonDemo04 singletonDemo04;

 

     privateSingletonDemo04() {

 

     }

 

     publicSingletonDemo04 getInstance() {

           if(singletonDemo04== null) {

                synchronized (this) {

                     if(singletonDemo04== null) {

                          singletonDemo04 = newSingletonDemo04();

                     }

                }

           }

           returnsingletonDemo04;

     }

 

}

 

单例防止反射漏洞攻击

在构造函数中,只能允许初始化化一次即可。

private static booleanflag = false;

 

     privateSingletonDemo04() {

 

           if (flag == false) {

                flag = !flag;

           } else {

                throw newRuntimeException("单例模式被侵犯!");

           }

     }

 

     public static voidmain(String[] args) {

 

     }

 

 

如何选择单例创建方式

如果不需要延迟加载单例,可以使用枚举或者饿汉式,相对来说枚举性好于饿汉式。

如果需要延迟加载,可以使用静态内部类或者懒韩式,相对来说静态内部类好于懒韩式。

猜你喜欢

转载自blog.csdn.net/sinat_16658263/article/details/89293015
今日推荐