一篇文章教你使用枚举来实现java单例模式
作者:蛋挞学姐 发布时间:2023-08-23 22:24:36
传统的单例写法解决了什么问题
首先,在大多数情况下(不包含面试),传统的单例写法已经完全够用了。通过 synchronized 关键字解决了多线程并发使用。
public synchronized static SingleClassV1 getInstance(){
if(instance == null){
instance = new SingleClassV1();
}
return instance;
}
考虑到每次获取单例对象都需要加锁,解锁。又有人发明了双重锁校验 + volatile 关键字模式:
private static volatile SingleClassV2 instance;
public static SingletonV2 getInstance() {
if(instance == null){
synchronized (SingletonV2.class){
if(instance == null){
instance = new SingletonV2();
}
}
}
return instance;
}
另外一种为了解决单例被重复初始化的写法:利用类只会被初始化一次的特性,又有人发明出来一种内部类单例的写法。
private static class SingletonHolder {
private static final SingletonV3 INSTANCE = new SingletonV3();
}
public static final SingletonV3 getInstance() {
return SingletonHolder.INSTANCE;
}
仍然存在的问题
由于 java 中有反射 API 这种变态的存在,以上所有的私有构造方法在反射面前都是毛毛雨。
Class<?> clazzV2 = Class.forName(SingleClassV2.class.getName());
Constructor<?> constructor = clazzV2.getDeclaredConstructors()[0];
constructor.setAccessible(true);
Object o = constructor.newInstance();
看来私有方法是防君子不防小人
为什么枚举就没有问题
我们来先看一下基于枚举的单例是什么样的。
public enum SingleClassV4 {
INSTANCE;
public String doSomeThing(){
return "hello world";
}
}
当然,从 java 代码是看不出来任何端倪的。再使用 javap 看一下字节码。
public final class git.frank.SingleClassV4 extends java.lang.Enum<git.frank.SingleClassV4>
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_FINAL, ACC_SUPER, ACC_ENUM
可以发现,枚举类型会帮我们自动继承 java.lang.Enum 类。并且,在 flags 中该类被添加了 ACC_ENUM 标识。然后,再看一下枚举类的构造方法:
private git.frank.SingleClassV4();
descriptor: (Ljava/lang/String;I)V
flags: ACC_PRIVATE
Code:
stack=3, locals=3, args_size=3
0: aload_0
1: aload_1
2: iload_2
3: invokespecial #6
// Method java/lang/Enum."<init>":(Ljava/lang/String;I)V
6: return
LineNumberTable:
line 3: 0//加入Java开发交流君样:756584822一起吹水聊天
LocalVariableTable:
Start Length Slot Name Signature
0 7 0 this Lgit/frank/SingleClassV4;
Signature: #29 // ()V
枚举类也是要有构造方法的,而且也和普通的类没什么不同,也一样可以通过反射获取到:
接下来,让我们通过反射 invoke 一下他的构造方法看看会发生什么:
constructor.newInstance();
结果如下:
Exception in thread "main" java.lang.IllegalArgumentException: Cannot reflectively create enum objects at java.lang.reflect.Constructor.newInstance(Constructor.java:417)
通过看 newInstance 方法代码的话,就很容易知道原因了:
public T newInstance(Object ... initargs)
throws InstantiationException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException
{//加入Java开发交流君样:756584822一起吹水聊天
...
if ((clazz.getModifiers() & Modifier.ENUM) != 0)
throw new IllegalArgumentException("Cannot reflectively create enum objects");
...
T inst = (T) ca.newInstance(initargs);
return inst;
}
java 的反射 API 在创建对象实例是判断了当前类是否是枚举类型,否则就抛异常出来。
来源:https://blog.csdn.net/wj1314250/article/details/117260150


猜你喜欢
- 一、前言最近写了个项目,前端还没写,需要部署到服务器给女朋友实现前端,可是不熟悉Linux的我,蹑手蹑脚,真的是每一步都是bug,可谓是步步
- 1.pom文件示例2.执行mvn package出现异常mvn package3.异常堆栈详细信息[WARNING] Error injec
- 很久以来都想研究一下利用java操作Excel的方法,今天没事,就稍微了解了一下,特总结一下。利用java操作Excel,有个开源的东东-j
- 情景描述将一个时间转换为对应的unix时间戳,字符集使用UTF-8编码,数据通讯统一采用 HTTP 协议通讯,使用POST 方法请求并传递参
- 实现代码超简单,具体实现方法如下:有时候当我们的游戏人物遇敌时,我们需我怪物随机根据概率选择处理方式,如下:1、50%的机会友好的问候2、2
- 1.什么是逆向工程mybaits需要程序员自己编写sql语句,mybatis官方提供逆向工程 可以针对单表自动生成mybatis执行所需要的
- 本文实例讲述了Java编程实现统计一个字符串中各个字符出现次数的方法。分享给大家供大家参考,具体如下:import java.util.It
- java * 的方法总结AOP的拦截功能是由java中的 * 来实现的。说白了,就是在目标类的基础上增加切面逻辑,生成增强的目标类(该
- 参考链接IDEA 2020.2.3版本IntelliJ IDEA 2020.2.3永久激活码(亲测有效)IDEA 2019.3版本Intel
- 一、前言前一篇文章已经详细介绍了如何使用Xposed框架编写第一个微信插件:摇骰子和猜拳 * 本文继续来介绍如何使用Xposed框架编写第
- 这篇文章主要介绍了Java多线程的临界资源问题解决方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋
- 目录:DioManager:Dio辅助类NWMethod:请求方法,get、post等NWApi:大家都知道EntityFactory:js
- 本文详细讲述了Android平台基于Pull方式对XML文件解析与写入方法。分享给大家供大家参考,具体如下:XML技术在跨平台的情况下的数据
- 为了避免直接进入项目中存在的页面,使用filter过滤器新建一个类loginFilter:package com.tjcu.filter;i
- 本文实例为大家分享了java文件处理工具类的具体代码,供大家参考,具体内容如下import java.io.BufferedInputStr
- 1.包1.包的三大作用区分相同名字的类当类很多时,可方便管理控制访问范围2.包的基本语法package abc.www;3.包的本质实际上就
- 本文实例为大家分享了java实现简单单链表的具体代码,供大家参考,具体内容如下一、定义:单链表是一种链式存取的数据结构,用一组地址任意的存储
- 统计图形种类繁多, 有柱状图, 折线图, 扇形图等等, 而统计图形的绘制方法也有很多, 有Flash制作的统计图形, 有水晶报表生成统计图形
- 使用 replace 函数动态填充字符串String str="Hello {0},我是 {1},今年{2}岁"
- 目录前言简要1.Launcher向AMS发送启动Activity2.AMS启动Activity并通知Launcher进入Paused状态3.