• java的设计模式


    java 面试中单例模式基本都是必考的,下面记录一下

    饿汉式

    public class Singleton {
    
        private static final Singleton instance = new Singleton();
    
        private Singleton(){}
        public static Singleton getInstance(){
            return instance;
        }
    }
    

    其实真心觉得没什么问题

    • private Singleton 来修饰可以防止创建多个实例
    • 没有延迟加载?这是需求不同好吗!有很多的需求是希望一开始就加载好的,不希望要用的时候再加载的,比如是java.lang.Runtime

    但面试的时候,你不能这样回答,面试官不开心的,你要回答

    • 没有延迟加载
    • 譬如 Singleton 实例的创建是依赖参数或者配置文件的,在 getInstance() 之前必须调用某个方法设置参数给它,是不能使用。(在构造函数上传参不行吗?,不考虑延迟加载)

    懒汉模式

    public class Singleton {
        private volatile static Singleton instance; //声明成 volatile
        private Singleton (){}
        public static Singleton getSingleton() {
            if (instance == null) {                         
                synchronized (Singleton.class) {
                    if (instance == null) {       
                        instance = new Singleton();
                    }
                }
            }
            return instance;
        }
       
    }
    

    你看加个延迟加载

    • 要将 instance 声明个 volatile 好让在多线程的环境下可见
    • 又要注意在多线程的情况下要主要加锁,因为会出现一个线程认为是空要构造对象,而另一个对象也认为是空要构造对象是情况

    麻烦!!!
    但也有应用场景的,在资源占用很多,又不常用的情况下,可以考虑用懒汉模式

    懒汉式二式 —— 内部静态类

    public class Singleton {
        private final static class SingleHolder {
            private static final Singleton INSTANCE = new Singleton();
        }
    
        private Singleton() {
        }
    
        public static Singleton getInstance() {
            return SingleHolder.INSTANCE;
        }
    }
    

    因为这个是 JVM 保证其线性安全性的,而且会在加载的时候创建,又不依赖版本,所以以前会比较推荐。

    致命的危险

    其实上面的东东都有致命的危险!反射
    上面按正常人类的做法是不会产生多个实例,如果会产生多个实例是说明上面的方式或多或少不够完美 ,比如反射

    @Test
    public void test() throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        Constructor<?> cls = Singleton.class.getDeclaredConstructor();
        cls.setAccessible(true);
        Singleton singleton = (Singleton) cls.newInstance();
        assertNotEquals(singleton,Singleton.getInstance());//明显这两个对象是不一样的!!!
    }
    

    也就是说上面的方式面对复杂的序列化或者反射攻击,可能会出现问题!(当然要先实现 Serializable 接口)
    比如:

    @Test
    public void test() throws ClassNotFoundException,  IOException {
        //序列化对象到文件
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("singleton"));
        objectOutputStream.writeObject(Singleton.getInstance());
        //从文件中读取对象
        File file = new File("singleton");
        ObjectInputStream objectInputStream =  new ObjectInputStream(new FileInputStream(file));
        Singleton singleton = (Singleton) objectInputStream.readObject();
        //判断是否是同一个对象
        assertNotEquals(singleton,Singleton.getInstance());
    }
    

    当然也有应对序列化的方式。
    就是在类中添加readResolve函数,因为反序列化中,如果存在这个函数,序列化的结果就是这函数的值。
    所以你在要序列化中的类添加这句就可以了。

    private Object readResolve() {
        return getInstance();
    }
    

    但面对反射就确实无力了。

    最好的方式 - 枚举类

    public enum SingletonEnum {
        INSTANCE;
    }
    
    • 面对反射:jvm 直接禁止了通过反射构造枚举实例的行为!
    • 面对序列化:jvm 对 enum 类的序列化,不是调用 writeObject、readObject 这些方法的。结果是就算是序列化后再反序列化,结果都是一样。

    以上

  • 相关阅读:
    Linux常用命令(5)--SSH访问远程服务器、SCP服务器间文件拷贝
    【转载】善用工具(1)--Mac版UltraEdit编辑器破解方法
    Linux常用命令(4)--善用"help"、"man在线帮助文档",轻松搞定系统命令
    Linux常用命令(3)--文件管理(查看文件大小权限信息、修改文件所属用户和操作权限、压缩解压文件)
    Linux常用命令(2)--vi (vim)文本编辑工具
    Linux常用命令(1)--用户管理(添加用户、修改密码、授予root权限)
    30分钟掌握ES6/ES2015核心内容(下)
    30分钟掌握ES6/ES2015核心内容(上)
    99%的人都理解错了HTTP中GET与POST的区别
    js中const,var,let区别
  • 原文地址:https://www.cnblogs.com/jojo-feed/p/10159624.html
Copyright © 2020-2023  润新知