写点什么

Java 单例模式实现,一次性学完整,面试加分项

  • 2022 年 4 月 23 日
  • 本文字数:1812 字

    阅读完需:约 6 分钟

private volatile static Singleton03 instance;


/**


  • 私有构造方法


*/


private Singleton03(){}


public static Singleton03 getInstance() {


if(instance == null) {


synchronized (Singleton03.class) {


if (instance == null) {


instance = new Singleton03();


}


}


}


return instance;


}


}


《一线大厂 Java 面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义》无偿开源 威信搜索公众号【编程进阶路】


使用到了 volatile 机制。这个是第二种方式的升级版,俗称双重检查锁定。既保证了效率,又保证了安全。

[](()4)饿汉模式

public class Singleton03 {


private static Singleton03 instance = new Singleton03();


/**


  • 私有构造方法


*/


private Singleton03(){}


public static synchronized Singleton03 getInstance() {


return instance;


}


}


**这种基于类加载机制避免了多线程的同步问题,初始化的时候就给装载了。但却没了懒加载的效果。


这也是最简单的一种实现。**

[](()5)静态内部类

public class Singleton04 {


// 静态内部类


private static class SingletonHolder {


private static final Singleton04 INSTANCE = new Singleton04();


}


/**


  • 私有构造方法


*/


private Singleton04(){}


public static Singleton04 getInstance() {


return SingletonHolder.INSTANCE;


}


}


**这种方式当Singleton04类被加载时,其内部类并不会被加载,所以单例类INSTANCE不会被初始化。


只有显式调用getInstance方法时,才会加载SingletonHolder,从而实例化INSTANCE


由于实例的建立是在类加载时完成,所以天生线程安全。因此兼备了懒加载和线程安全的特性。**

[](()6)枚举(号称最好)

public enum EnumSingleton01 {


INSTANCE;


public void doSomething() {


System.out.println("doSomething");


}


}


模拟数据库链接:


public enum EnumSingleton02 {


INSTANCE;


private DBConnection dbConnection = null;


private EnumSingleton02() {


dbConnection = new DBConnection();


}


public DBConnection getConnection() {


return dbConnection;


}


}


**这种方式是Effective Java作者Josh Bloch提倡的方式,它不仅能避免多线程同步问题,


而且还能防止反序列化重新创建新的对象。**

[](()2、为什么说枚举方法是最好的?

前 5 种方式实现单例都有如下 3 个特点:


  • 构造方法私有化

  • 实例化的变量引用私有化

  • 获取实例的方法共有


首先,私有化构造器并不保险。因为它抵御不了反射攻击,其次就是序列化重新创建新对象。下面来进行验证。

[](()1) 反射验证

@Test


public void reflectTest() throws Exception {


Singleton03 s = Singleton03.getInstance();


// 拿到所有的构造函数,包括非 public 的


Constructor<Singleton03> constructor = Singleton03.class.getDeclaredConstructor();


constructor.setAccessible(true);


// 构造实例


Singleton03 reflection = constructor.newInstance();


System.out.println(s);


System.out.println(reflection);


System.out.println(s == reflection);


}


输出结果:


org.yd.singleton.Singleton03@61e4705b


org.yd.singleton.Singleton03@50134894


false


再看看枚举类的测试


@Test


public void reflectEnumTest() throws Exception {


EnumSingleton01 s = EnumSingleton01.INSTANCE;


// 拿到所有的构造函数,包括非 public 的


Constructor<EnumSingleton01> constructor = EnumSingleton01.class.getDeclaredConstructor();


constructor.setAccessible(true);


// 构造实例


EnumSingleton01 reflection = constructor.newInstance();


System.out.println(s);


System.out.println(reflection);


System.out.println(s == reflection);


}


输出结果:


java.lang.NoSuchMethodException: org.yd.singleton.EnumSingleton01.<init>()


at java.lang.Class.getConstructor0(Class.java:3082)


at java.lang.Class.getDeclaredConstructor(Class.java:2178)


at org.yd.singleton.SingletonTest.reflectEnumTest(SingletonTest.java:61)


at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)


at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)


at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)


结论:通过反射,单例模式的私有构造方法也能构造出新对象。不安全。而枚举类直接抛异常,说明枚举类对反射是安全的。

[](()2) 序列化验证

@Test


public void serializeTest(){


Singleton03 s = Singleton03.getInstance();

用户头像

还未添加个人签名 2022.04.13 加入

还未添加个人简介

评论

发布
暂无评论
Java单例模式实现,一次性学完整,面试加分项_Java_爱好编程进阶_InfoQ写作社区