当前位置: 首页 > 科技观察

面试官:说说对单例模式的理解,最后的枚举实现我居然不知

时间:2023-03-22 11:07:20 科技观察

面试官:谈谈你对单例模式的理解。我不知道最终的枚举实现。转载本文请联系Java极客技术公众号。1.简介说起单例模式,想必大家都不陌生。它是Java中最简单的设计模式之一,是一种创建型模式。它提供了以最简单的方式创建对象的最佳方式。这种模式的意义在于保证一个类只有一个实例,并提供一个全局访问点来访问它,避免重复创建对象,节省系统资源。2.实现思路创建一个类,并将其默认构造方法私有化,使得外界无法通过newObject获取对象实例,同时对外提供获取对象唯一实例的方法。比如创建一个SingleObject,如下:publicclassSingleObject{//创建一个SingleObject的对象privatestaticSingleObjectinstance=newSingleObject();//将构造函数设为private,这样该类就不会被实例化privateSingleObject(){}//获取唯一可用的publicstaticSingleObjectgetInstance(){returninstance;}publicvoidshowMessage(){System.out.println("HelloWorld!");}}从单例类中获取唯一对象publicclassSingletonPatternDemo{publicstaticvoidmain(String[]args){//非法构造函数//编译时错误:constructorSingleObject()isinvisible//SingleObjectobject=newSingleObject();//获取唯一可用的对象SingleObjectobject=SingleObject.getInstance();//显示消息对象.showMessage();}}执行程序,输出结果为:HelloWorld!三、单例模式的几种实现方式3.1.惰性风格,线程不安全(不推荐)这种方式是最基本的实现方式,这种实现最大的问题是不支持多线程。因为没有锁synchronized,严格来说不是单例模式。这种方式的懒加载很明显,不需要线程安全,在多线程中无法正常工作。publicclassSingleton{privatestaticSingletoninstance;privateSingleton(){}publicstaticSingletongetInstance(){if(instance==null){instance=newSingleton();}returninstance;}}3.2Lazy,线程安全(不推荐)这种方法有很好的Lazyloadingcan在多线程中工作良好,但效率很低,99%的情况下不需要同步。优点:只在第一次调用后才初始化,避免了内存浪费。缺点:synchronized必须加锁,保证单例,加锁会影响效率。getInstance()的性能对应用程序并不重要(该方法很少使用)。publicclassSingleton{privatestaticSingletoninstance;privateSingleton(){}publicstaticsynchronizedSingletongetInstance(){if(instance==null){instance=newSingleton();}returninstance;}}3.3饿了么中国式(推荐)这种方法比较常用,但是简单产生垃圾对象。优点:没有锁,执行效率会提高。缺点:类加载时初始化,浪费内存。它基于类加载器机制避免了多线程同步问题。但是,实例是在加载类时实例化的。类加载的原因虽然有很多,但大多是在单例模式下调用了getInstance方法,但不确定是否有其他方式(或其他静态方法)导致类加载。这时候初始化实例显然达不到懒加载的效果。publicclassSingleton{privatestaticSingletoninstance=newSingleton();privateSingleton(){}publicstaticSingletongetInstance(){returninstance;}}3.4.Doublechecklock/doublechecklock(recommended)这种方式采用双锁机制,安全,多线程也能保持高性能。getInstance()的性能对应用程序至关重要。publicclassSingleton{privatevolatilestaticSingletonsingleton;privateSingleton(){}publicstaticSingletongetSingleton(){if(singleton==null){synchronized(Singleton.class){if(singleton==null){singleton=newSingleton();}}}returnssingleton;}}3.5,静态内部类(推荐)这种方法可以达到和doublechecklock方法一样的效果,对静态字段使用惰性初始化,但是实现更简单。该方法只适用于静态字段,当需要惰性初始化实例字段时可以使用double-checklock方法。publicclassSingleton{privatestaticclassSingletonHolder{privatestaticfinalSingletonINSTANCE=newSingleton();}privateSingleton(){}publicstaticfinalSingletongetInstance(){returnSingletonHolder.INSTANCE;}}3.6。枚举(推荐)这种实现方式还没有被广泛采用,但这是实现单例模式的最佳方式。更简洁,自动支持序列化机制,绝对杜绝多次实例化。这种方法是由EffectiveJava的作者JoshBloch提倡的。不仅可以避免多线程同步问题,而且自动支持序列化机制,防止反序列化重新创建新对象,绝对防止多重实例化。但是由于在JDK1.5之后增加了枚举特性,这样写是在所难免的,在实际工作中也很少用到。publicenumSingleton{INSTANCE;publicvoiddoMethod(){}}第四,单例模式在Java中的应用非常多。例如,Runtime就是一个典型的例子。源码如下:publicclassRuntime{privatestaticRuntimecurrentRuntime=newRuntime();/***返回与当前Java应用相关联的运行时对象。*Runtime类的大部分方法都是实例*方法,必须相对于当前运行时对象调用.**@returntheRuntimeobjectassociatedwiththecurrent*Javaapplication.*/publicstaticRuntimegetRuntime(){returncurrentRuntime;}/**Don'tletanyoneelseinstantiatethisclass(Runtime){privatethisclass*}...}很清楚单例对象是使用hungry方法创建的!5.总结总的来说,不推荐使用第一种和第二种偷懒方法,推荐使用第三种饿法。第五种注册方式只有在显式要达到懒加载效果时才使用。如果涉及反序列化创建对象,可以尝试使用第六种枚举方式。如果有其他特殊需求,可以考虑使用第四种双重检查锁法。