zoukankan      html  css  js  c++  java
  • 用私有构造器或者枚举类型强化Singleton 属性

    用私有构造器或者枚举类型强化Singleton 属性

    1. Singleton 的含义

    Singleton 大部分人都知道是单例的意思,因为它一般都是我们理解设计模式时,最好理解的一个。它的作用就是 使这个类就被实例化一次。在JDK1.5 前实现有两种方式,分别是 懒汉模式饿汉模式

    1.1 懒汉模式

    1. /** 
    2. * 懒汉模式 
    3. */ 
    4. public class Sengleton_Lyze
    5. //1,构造方法私有化 
    6. private Sengleton_Lyze(){  

    7.  
    8. //2,创建类的位唯一实例,private static 修饰 
    9. private static Sengleton_Lyze instance; 
    10.  
    11. //3,提供获取实例的方法,public static 修饰 
    12. public static Sengleton_Lyze getInstance()
    13. if (instance==null) { 
    14. instance=new Sengleton_Lyze(); 

    15. return instance; 


    为什么使用getInstance()方法来获得实例?

    • 它可以被很灵活的修改,用户在不需要修改API的情况下,我们可以改变这个类是否是单例的。

    • 第二个优势在泛型

    1.2 饿汉模式

    1. /** 
    2. * 单例模式Sengleton 
    3. * 试用实际场合:有些对象只需要一个就够了。 
    4. * 作用:保证整个实际应用程序中某个实例有且只有一个 
    5. * 类别:饿汉模式,懒汉模式 
    6. * 区别:饿汉模式的特点,加载类时比较慢,获取比较慢。线程安全的。 
    7. *  
    8. *  
    9. */ 
    10. public class Sengleton_Hunger
    11. //1,构造方法私有化,不允许外接直接创建对象 
    12. private Sengleton_Hunger(){  

    13. //2,创建类的唯一实例,使用private static 修饰 
    14. private static Sengleton_Hunger instance = new Sengleton_Hunger(); 
    15.  
    16. //3,获取这个实例的方法 
    17. public static Sengleton_Hunger getInstance()
    18. return instance; 

    19.  

    运行代码

    1. public class Test
    2. public static void main(String[] args)
    3.  
    4. //饿汉模式 
    5. Sengleton_Hunger s1=Sengleton_Hunger.getInstance(); 
    6. Sengleton_Hunger s2=Sengleton_Hunger.getInstance(); 
    7. if (s1==s2) { 
    8. System.out.println("true"); 
    9. }else
    10. System.out.println("false"); 

    11.  
    12. //懒汉模式 
    13. Sengleton_Lyze s3=Sengleton_Lyze.getInstance(); 
    14. Sengleton_Lyze s4=Sengleton_Lyze.getInstance(); 
    15. if (s1==s2) { 
    16. System.out.println("一样的"); 
    17. }else
    18. System.out.println("不同的"); 


    19.  

    运行结果

    true
    一样的
    

    1.3 序列化

    为了让成为单例的类实现 序列化的(Serializable),我们仅仅在声明上加上 "implements Serializable" 是不够的。 为了维护并保证 单例,必须要声明所有实例域都是 瞬时(transient) 的,并提供一个readResolve方法。否则,每次反序列化一个序列化的实例时,都会创建一个新的实例。如,我们可以为我们的 懒汉模式 加上这个方法

    1. private Object readResolve()
    2. return instance; 

    1.4 总结

    • 都需要构造方法私有化

    • 提供公有的静态成员,方便客户端获取类的唯一实例

    • 序列化时需要提供额外的readResolve 方法,防止反序列化时创建对象
      总体上来说,懒汉就是时间换空间,在需要获取的时候才会去实例化。而饿汉就是空间换时间,不管使用否,都会实例化一个放入内存中。饿汉模式的特点,加载类时比较慢,获取比较慢、线程安全的,饿汉反之。
      但是如果客户端拥有 AccessibleObject.setAccessible() 的权限,就可以通过反射机制调用私有构造器

    2 通过反射获得不同实例

    2.1 举例说明

    1. public class Test
    2. public static void main(String[] args) throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException
    3. //饿汉模式 
    4. Constructor sengleton_Hunger = Sengleton_Hunger.class.getDeclaredConstructor(); 
    5. sengleton_Hunger.setAccessible(true); 
    6. Sengleton_Hunger s1= (Sengleton_Hunger) sengleton_Hunger.newInstance(); 
    7. Sengleton_Hunger s2= (Sengleton_Hunger) sengleton_Hunger.newInstance(); 
    8. if (s1==s2) { 
    9. System.out.println("true"); 
    10. }else
    11. System.out.println("false"); 

    12.  
    13. //懒汉模式 
    14. Constructor sengleton_Lyze = Sengleton_Lyze.class.getDeclaredConstructor(); 
    15. sengleton_Lyze.setAccessible(true); 
    16. Sengleton_Lyze s3= (Sengleton_Lyze) sengleton_Lyze.newInstance(); 
    17. Sengleton_Lyze s4= (Sengleton_Lyze) sengleton_Lyze.newInstance(); 
    18. if (s1==s2) { 
    19. System.out.println("一样的"); 
    20. }else
    21. System.out.println("不同的"); 


    22.  

    显示的结果

    false
    不同的
    

    2.2 通过异常来防止二次实例对象

    public class Sltn {
        private static Sltn s = null;
        private static boolean flag = true;
        
        private Sltn(){
            System.out.println("flag:" + flag);
            if(flag){  
                flag = !flag;  
            }else{  
                try {  
                    throw new Exception("duplicate instance create error!" + Sltn.class.getName());  
                } catch (Exception e) {  
                    e.printStackTrace();  
                }  
            }  
        }
        
        public static Sltn getInstance(){
            if(null == s){  
                s = new Sltn();  
            }  
            return s;  
        }
    }
    

    使用反射第二次创建就会抛出异常

    2.3 总结

    • 通过反射在一定的条件下,是可以使用 类私有的构造方法来获得不同对象的。

    • 通过在类私有构造方法中加入条件,可以防止反射获得第二个实例,但写法有些多余、复杂。

    3. 枚举类型

    在JDK1.5之后出现了第三种实现单例模式的方式 ,只需要编写一个含有单个元素的枚举类型

    public enum Elvis {
        INSTANCE;
        public void leaveTheBuilder(){
        }
    }
    
    • 这种方法更加的简洁

    • 无偿提供序列化机制,绝对防止多次实例化

    • 可以面对复杂的序列化或者反射攻击

    • 单元素的枚举类型已经成为了实现Singleton(单例)的最佳方法

  • 相关阅读:
    Laravel使用Eloquent ORM操作数据库
    Laravel查询构造器的使用方法整理
    [wordpress]后台自定义菜单字段和使用wordpress color picker
    Thinkphp kindeditor 内容转义
    WordPress 后台提示输入FTP信息
    [记录]gulp compass
    symonfy 项目根目录下没有 bin/console 文件的解决方法
    [gulp] gulp lint 忽略文件
    [转载]ubuntu Atheros Communications Device 1083 驱动
    SharpDevelop 编译时,任务失败,因为未找到“resgen.exe”的解决方法
  • 原文地址:https://www.cnblogs.com/ybbzbb/p/5524261.html
Copyright © 2011-2022 走看看