资讯专栏INFORMATION COLUMN

我的面试准备过程--单例模式(更新中)

you_De / 1511人阅读

摘要:把准备过程纪录下来,共勉。单例模式有几种经典写法,核心思想就是将构造函数私有化,并且通过静态方法获取一个唯一的实例。

写在最前面

导师贪腐出逃美国,两年未归,可怜了我。拿了小米和美团的offer,要被延期,offer失效,工作重新找。把准备过程纪录下来,共勉。

单例模式有几种经典写法,核心思想:就是将构造函数私有化,并且通过静态方法获取一个唯一的实例。

饿汉式
    public class Singleton{
        private Singleton(){}
        
        private static Singleton instance = new Singleton();
        
        public static Singleton getInstance(){
            return instance;
        }
    }
懒汉式
    public class Singleton{
        private Singleton(){}
        
        private static Singleton instance = null;
        
        public static Singleton getInstance(){
            instance = new Singleton();
            return instance;
        }
    }
双锁式
    public class Singleton{
        private Singleton(){
        }
        
        private volatile static Singleton instance = null;
        
        public static Singleton getInstance(){
            if(instance == null){
                synchonized(Singleton.class){
                    if(instance == null){
                        instance = new Singleton();
                    }
                }
            }
            return instance;
        }
    }

关于volatile关键字
上面添加了volatile关键字,如果没有volatile关键字,在执行instance = new Singleton()时可能会出现问题,伪代码如下:

inst = allocat();   // 第一步:分配内存  
constructor(inst);  // 第二步:执行构造函数  
instance = inst;    // 第三步:赋值,将instance对象指向分配的内存空间(此时instance就不是null了)  

由于Java编译器允许处理器乱序执行,所以第二步和第三步的顺序无法保证。如果第三步先执行完毕、第二步未执行时,有另外的线程调用了instance,由于已经赋值,将判断不为null,拿去直接使用,但其实构造函数还未执行,成员变量等字段都未初始化,直接使用,就会报错。这就是DCL失效问题,而且很难复现。

对volatile变量的写操作,不允许和它之前的读写操作打乱顺序;对volatile变量的读操作,不允许和它之后的读写乱序。

当一个线程要使用共享内存中的volatile变量时,它会直接从主内存中读取,而不是使用自己本地内存中的副本。当一个线程对一个volatile变量进行写时,它会将这个共享变量值刷新到共享内存中。

本节参考 http://blog.csdn.net/hxpjava1...

枚举类
    public enum Singleton{
        instance;
    }
关于序列化、反序列化

一个enum常量(这里是INSTANCE)代表了一个enum的实例,enum类型只能有这些常量实例。标准保证enum常量(INSTANCE)不能被克隆,也不会因为反序列化产生不同的实例,想通过反射机制得到一个enum类型的实例也不行的。

避免序列化、反序列化时产生多个实例,有两种解决方法

抛出异常

/** 
 * prevent default deserialization 
 */  
private void readObject(ObjectInputStream in) throws IOException,  
   ClassNotFoundException {  
       throw new InvalidObjectException("can"t deserialize enum");  
}  

在readResolve()方法中返回实例

ANY-ACCESS-MODIFIER Object writeReplace() throws ObjectStreamException;  

ANY-ACCESS-MODIFIER Object readResolve() throws ObjectStreamException;  

以上两个方法可以理解为序列化和反序列化过程的入口和出口。writeReplace()返回的对象,就是要被序列化的对象,我们有机会在序列化前把这个对象给换成我们确定好的那个(如果不是“故意捣乱”,暂时没想到有什么用);而readResolve()方法就是在反序列化完成得到对象前,把这个对象给换成我们确定好的那个。

/** 
 * 反序列化时内存Hook这段代码 
 * @return 
 */  
private Object readResolve() {  
    return instance;  
}  

本节参考 http://837062099.iteye.com/bl...

静态内部类

内部类的机制来巧妙实现懒汉式单例模式的实现 :Lazy initialization holder class模式。

内部类分为对象级别类级别,类级内部类指的是,有static修饰的成员变量的内部类。如果没有static修饰的成员变量的内部类被称为对象级内部类。

类级内部类相当于其外部类的static成员,它的对象与外部类对象间不存在依赖关系,相互独立,因此可直接创建。而对象级内部类的实例,是必须绑定在外部对象实例上的。类级内部类只有在第一次被使用的时候才被会装载。采用类级内部类,在这个类级内部类里面去创建对象实例,能够让类装载的时候不去初始化对象。

当getInstance方法第一次被调用的时候,它第一次读取SingletonHolder.instance,内部类SingletonHolder类得到初始化;而这个类在装载并被初始化的时候,会初始化它的静态域,从而创建Singleton的实例,由于是静态的域,因此只会在虚拟机装载类的时候初始化一次,并由虚拟机来保证它的线程安全性。

这个模式的优势在于,getInstance方法并没有被同步,并且只是执行一个域的访问,因此延迟初始化并没有增加任何访问成本。

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

本节参考 http://blog.csdn.net/hikvisio...

全文参考 http://blog.csdn.net/u0129753...

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/70130.html

相关文章

  • 求职准备 - 收藏集 - 掘金

    摘要:一基础接口的意义百度规范扩展回调抽象类的意义想不想通过一线互联网公司面试文档整理为电子书掘金简介谷歌求职记我花了八个月准备谷歌面试掘金原文链接翻译者 【面试宝典】从对象深入分析 Java 中实例变量和类变量的区别 - 掘金原创文章,转载请务必保留原出处为:http://www.54tianzhisheng.cn/... , 欢迎访问我的站点,阅读更多有深度的文章。 实例变量 和 类变量...

    cuieney 评论0 收藏0
  • 一个JAVA渣渣的校招成长记,附BAT美团网易等20家面经总结

    摘要:作者重庆森林链接来源牛客网整个三月份通过牛客网和网友分享的经验学到了很多东西,现在反馈一下我的面试经历,希望对同学们有帮助。个人情况大三本方向渣硕,经过实验室学长内推,于三月底完成面试。校招是实力和运气的结合,缺一不可。 欢迎关注我的微信公众号:Java面试通关手册(坚持原创,分享美文,分享各种Java学习资源,面试题,以及企业级Java实战项目回复关键字免费领取):showImg(h...

    mozillazg 评论0 收藏0
  • 又被面试官问设计模式了,我真的是

    摘要:面试官要不你来手写下单例模式呗候选者单例模式一般会有好几种写法候选者饿汉式简单懒汉式在方法声明时加锁双重检验加锁进阶懒汉式静态内部类优雅懒汉式枚举候选者所谓饿汉式指的就是还没被用到,就直接初始化了对象。面试官:我看你的简历写着熟悉常见的设计模式,要不你来简单聊聊你熟悉哪几个吧?候选者:常见的工厂模式、代理模式、模板方法模式、责任链模式、单例模式、包装设计模式、策略模式等都是有所了解的候选者:...

    不知名网友 评论0 收藏0
  • 来自投资银行的 10 个 Java 面试

    摘要:本文介绍一些来自投资银行的针对三年以上经验的开发人员面试题。第七题和这两个方法有什么不同答案本题取自我的投资银行针对有经验的开发者的五十个多线程面试题列表。总结以上就是投资银行通常会出的面试题。 原文地址: https://dzone.com/articles/10... 有为数不少的开发者希望能在像 Barclays、Credit Suisse、Citibank 等等那样的投资银行做...

    xbynet 评论0 收藏0
  • Java - 收藏集 - 掘金

    摘要:强大的表单验证前端掘金支持非常强大的内置表单验证,以及。面向对象和面向过程的区别的种设计模式全解析后端掘金一设计模式的分类总体来说设计模式分为三大类创建型模式,共五种工厂方法模式抽象工厂模式单例模式建造者模式原型模式。 强大的 Angular 表单验证 - 前端 - 掘金Angular 支持非常强大的内置表单验证,maxlength、minlength、required 以及 patt...

    XiNGRZ 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<