zoukankan      html  css  js  c++  java
  • Java 单例设计模式

    1、懒汉模式

    class LazySingleton{
    
        //volatile防止指令重排
        private static volatile LazySingleton SINGLETON ;
    
        //构造方法私有化
        private LazySingleton(){}
    
        /*
          双重检查锁DCL(Double Check Lock)
          检查两次是否为null,再加一把锁
         */
         public static LazySingleton getInstance(){
            if (SINGLETON == null){
                synchronized (LazySingleton.class){
                    if (SINGLETON == null){
                        SINGLETON = new LazySingleton();
                    }
                }
            }
    
            return SINGLETON;
        }
    
    }
    

    为什么加锁?

    保证线程安全,防止多线程情况下多个线程同时执行新建对象方法。

    为什么不使用synchronized直接锁getInstance()方法?

    同一时间只能有一个线程访问方法,效率低下。

    为什么加volatile关键字?

    防止执行new MySingleton()时发生指令重排。

    2、饿汉模式

    /**
     * 饿汉式--单例模式
     */
    class HungrySingleton{
        
        //类加载的是否就初始化了,不用担心线程安全
        private static HungrySingleton SINGLETON  = new HungrySingleton();
    
        //构造方法私有化
        private HungrySingleton(){}
    
        public static HungrySingleton getInstance(){
            return SINGLETON;
        }   
    }
    

    缺点:
    实例在类初始化一开始就被创建了,哪怕后来根本没有使用它。

    3、静态内部类

    /**
     * 静态内部类(懒汉式)--单例模式
     */
    class InnerClassSingleton{
    
        //静态内部类创建对象
        private static class InnerClass{
            private static InnerClassSingleton singleton = new InnerClassSingleton();
        }
    
        private InnerClassSingleton(){}
    
        public static InnerClassSingleton getInstance(){
            return InnerClass.singleton;
        }
    }
    

    通过JVM类加载来解决线程安全。
    缺点:可以通过反射创建对象

    4、反射攻击

    我们通过静态内部类创建单例对象。虽然我们的构造方法是private的,当时仍然可以通过狗仔方法创建对象。

    public class TestDemo3 {
        public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
            //单例创建的对象
            System.out.println(InnerClassSingleton.getInstance());
            //通过反射创建对象
            Constructor<InnerClassSingleton> constructor = InnerClassSingleton.class.getDeclaredConstructor();
            constructor.setAccessible(true);
            InnerClassSingleton innerClassSingleton = constructor.newInstance();
            System.out.println(innerClassSingleton);
        }
    }
    
    com.zjw.InnerClassSingleton@1540e19d
    com.zjw.InnerClassSingleton@677327b6
    

    发现两个对象不一致。。

    解决办法:在构造方法处检查对象是否创建

    /**
     * 静态内部类(懒汉式)--单例模式
     * 解决反射创建问题
     */
    class InnerClassSingleton{
    
        //静态内部类创建对象
        private static class InnerClass{
            private static InnerClassSingleton singleton = new InnerClassSingleton();
        }
    
        //在构造方法检查对象是否创建
        private InnerClassSingleton(){
            if (InnerClass.singleton!=null){
                throw new RuntimeException("单例已经创建,不能再新建了");
            }
        }
    
        public static InnerClassSingleton getInstance(){
            return InnerClass.singleton;
        }
    }
    

    5、枚举类型

    /**
     * 枚举类型--单例模式
     */
    public enum EnumSingleton {
    
        SINGLETON;
    
        public void print(){
            System.out.println(this.hashCode());
        }
    }
    
    
    public class TestDemo4 {
        public static void main(String[] args) throws IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchMethodException {
            EnumSingleton singleton1 = EnumSingleton.SINGLETON;
            EnumSingleton singleton2 = EnumSingleton.SINGLETON;
            System.out.println(singleton1);
            System.out.println(singleton2);
            singleton1.print();
            singleton2.print();
    
            Constructor<EnumSingleton> constructor = EnumSingleton.class.getDeclaredConstructor(String.class, int.class);
            constructor.setAccessible(true);
            EnumSingleton singleton = constructor.newInstance("SINGLETON", 0);
        }
    }
    

    结果:

    SINGLETON
    SINGLETON
    356573597
    356573597
    Exception in thread "main" java.lang.IllegalArgumentException: Cannot reflectively create enum objects
    	at java.lang.reflect.Constructor.newInstance(Constructor.java:417)
    	at com.zjw.TestDemo4.main(TestDemo4.java:17)
    
    
    

    发现通过枚举类型创建的单例是不能通过反射创建的。

    6、序列化

    /**
     * 反序列化--单例模式
     */
    class HungrySingleton implements Serializable {
    
        private static final long serialVersionUID = 23423454L;
    
        private static HungrySingleton SINGLETON  = new HungrySingleton();
    
        //构造方法私有化
        private HungrySingleton(){}
    
        public static HungrySingleton getInstance(){
            return SINGLETON;
        }
    
        Object readResolve() throws ObjectStreamException{
            return SINGLETON;
        }
    
    }
    
    public class TestDemo2
    {
        public static void main(String[] args) throws IOException, ClassNotFoundException {
    
            HungrySingleton instance = HungrySingleton.getInstance();
    
            //序列化
            ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("instance"));
            outputStream.writeObject(instance);
            outputStream.close();
            //反序列化
            ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("instance"));
            HungrySingleton readSingleton = (HungrySingleton)inputStream.readObject();
            inputStream.close();
            //比较是否为同一个对象
            System.out.println(instance==readSingleton);
        }
    }
    

    不太懂,之后再研究~~~

    --------------- 我每一次回头,都感觉自己不够努力,所以我不再回头。 ---------------
  • 相关阅读:
    VMwarePlayer虚拟机下centos6的静态IP配置
    C/C++ 父子进程之间的文件描述符问题
    C++ wait捕捉的信号处理WIFEXITED/WEXITSTATUS/WIFSIGNALED
    WIN7下用笔记本创建无线网
    C++ readdir、readdir_r函数
    C++ int转string(stringstream可转更多类型)
    C/C++函数中使用可变参数
    C/C++中static关键字作用总结
    Unix网络编程第三版源码编译
    Linux下初次使用github
  • 原文地址:https://www.cnblogs.com/zjw-blog/p/13633974.html
Copyright © 2011-2022 走看看