Java中enum的静态成员的初始化

C++静态成员初始化七大陷阱与解决方案 在C++编程中,静态成员是与类关联而非对象实例的共享数据,其初始化规则涉及编译、链接和运行时等多个阶段,是理解C++对象模型和内存管理的关键概念。静态成员的初始化遵循严格的“一次定义规则”,必须在类外单独定义,否则会导致链接错误。其核心价值在于实现类级别的数据共享和状态管理,常用于配置参数、全局计数器、单例模式等场景。然而,静态初始化顺序问题(SIOF)是著名的未定义行为,当不同编译单元的静态对象存在依赖时,初始化顺序无法保证,可能导致程序崩溃。此外,在动态库、模板类等复杂场景中,静态成员的可见性和唯一性也 阅读详情
Java语言规范第三版8.9规定了enum里的构造器、初始化器和初始化块中不得引用该enum中非编译时常量的静态成员域。 
引用
It is a compile-time error to reference a static field of an enum type that is not a compile-time constant (§15.28) from constructors, instance initializer blocks, or instance variable initializer expressions of that type. It is a compile-time error for the constructors, instance initializer blocks, or instance variable initializer expressions of an enum constant e to refer to itself or to an enum constant of the same type that is declared to the right of e.


规范特别指出构造器与初始化器禁止访问静态成员域是为了禁止一些循环初始化的状况。例子是: 
Java Language Specification, 3rd 写道
Java代码   收藏代码
  1. enum Color {  
  2.     RED, GREEN, BLUE;  
  3.     static final Map<String,Color> colorMap = new HashMap<String,Color>();  
  4.     Color() {  
  5.         colorMap.put(toString(), this);  
  6.     }  
  7. }  

Java枚举类型中的枚举成员是静态成员,它们会首先被静态初始化;其它成员都只能在枚举成员之后声明,如果通过初始化器(如上例)来初始化的话,则开始初始化RED时静态变量colorMap尚未被赋值。 
初始化RED时要调用Color的构造器。如果允许构造器访问colorMap,就会对null调用了put()方法,于是遇到NullPointerException。 

规范认为添加了上述限制后就可以让这种循环初始化的代码无法编译,从而杜绝其造成运行时异常的问题。 

今天突然想起我前段时间才见到别人问过enum的初始化问题,而且就是遇到了静态初始化失败的错误。问了几个同学都说没问过我,稍微搜了下JavaEye问答频道也没看到。我还会是在哪里看到的呢,怪哉。我肯定是RP了…… 

想了会儿,总算构造出了记忆中见到的那种错误: 
Java代码   收藏代码
  1. import java.util.*;  
  2.   
  3. public class Demo {  
  4.     public static void main(String[] args) {  
  5.         PowerOfTwo i = PowerOfTwo.fromInt(2);  
  6.         System.out.println(i);  
  7.     }  
  8. }  
  9.   
  10. enum PowerOfTwo {  
  11.     ONE(1), TWO(2), FOUR(4), EIGHT(8);  
  12.       
  13.     private int value;  
  14.       
  15.     PowerOfTwo(int value) {  
  16.         this.value = value;  
  17.         registerValue(); //map.put(value, this);  
  18.     }  
  19.       
  20.     @Override  
  21.     public String toString() {  
  22.         return Integer.toString(this.value);  
  23.     }  
  24.       
  25.     private void registerValue() {  
  26.         PowerOfTwo.map.put(value, this);  
  27.     }  
  28.   
  29.     public static PowerOfTwo fromInt(int i) {  
  30.         return PowerOfTwo.map.get(i);  
  31.     }  
  32.       
  33.     private static final Map<Integer, PowerOfTwo> map = new HashMap<Integer, PowerOfTwo>();  
  34. }  

留意第17行,被注释掉的代码如果放进来就通不过编译,跟规范里提到的要避免的状况一样。但是把同样的逻辑放到了成员方法之后,我们就成功的看到了静态初始化错误: 
引用
Exception in thread "main" java.lang.ExceptionInInitializerError 
        at Demo.main(Demo.java:5) 
Caused by: java.lang.NullPointerException 
        at PowerOfTwo.registerValue(Demo.java:26) 
        at PowerOfTwo.<init>(Demo.java:17) 
        at PowerOfTwo.<clinit>(Demo.java:11) 
        ... 1 more

留意调用栈的状况。“... 1 more”没有显示出来的那个是Demo.main。它调用了PowerOfTwo枚举类型上的静态方法,引发了该类型的静态初始化(PowerOfTwo.<clinit>);其中,RED成员首先被初始化,调用构造器(PowerOfTwo.<init>);构造器则调用了成员方法registerValue来添加映射信息,访问到尚未被初始化到HashMap实例的静态成员域map,然后就出错了。 

也就是说上述限制的作用很有限……跟泛型有的一拼,呵呵。 

知道了问题没关系,只要问题有解决的办法就行。规范中也提供了Color例子的正确写法: 
Java Language Specification, 3rd 写道
Java代码   收藏代码
  1. enum Color {  
  2.     RED, GREEN, BLUE;  
  3.     static final Map<String,Color> colorMap = new HashMap<String,Color>();  
  4.     static {  
  5.         for (Color c : Color.values())  
  6.             colorMap.put(c.toString(), c);  
  7.     }  
  8. }  

关键点是在声明了静态成员域之后,在一个静态初始化块里来完成其内容的填充,而不要急着在构造器里就去做。当然要是在构造器里先判断一下null然后做合适的初始化也不是不行,但那样代码长了而且每构造一个实例都要检查一次,麻烦。原本需要针对每个实例做的初始化可以靠values()方法遍历所有的枚举成员来做。 

回到PowerOfTwo的例子,那就是改成: 
Java代码   收藏代码
  1. import java.util.*;  
  2.   
  3. public class Demo {  
  4.     public static void main(String[] args) {  
  5.         PowerOfTwo i = PowerOfTwo.fromInt(2);  
  6.         System.out.println(i);  
  7.     }  
  8. }  
  9.   
  10. enum PowerOfTwo {  
  11.     ONE(1), TWO(2), FOUR(4), EIGHT(8);  
  12.       
  13.     private int value;  
  14.       
  15.     PowerOfTwo(int value) {  
  16.         this.value = value;  
  17.     }  
  18.       
  19.     @Override  
  20.     public String toString() {  
  21.         return Integer.toString(this.value);  
  22.     }  
  23.   
  24.     public static PowerOfTwo fromInt(int i) {  
  25.         return PowerOfTwo.map.get(i);  
  26.     }  
  27.       
  28.     private static Map<Integer, PowerOfTwo> map = new HashMap<Integer, PowerOfTwo>();  
  29.     static {  
  30.         for (PowerOfTwo p : PowerOfTwo.values()) {  
  31.             PowerOfTwo.map.put(p.value, p);  
  32.         }  
  33.     }  
  34. }  


Effective Java, 2nd的Item 33有关于嵌套枚举类型的初始化的例子。 

这个例子教育我们写Java代码的时候顺序要注意清楚了,不然一个不小心就……|||| 
好吧我不是不写Java代码或者不写Java帖的。虽然现在用得不多,我还是得保持最低限度的熟练才行 T.T
*********************************华丽的分割线*********************************
转载自:http://rednaxelafx.iteye.com/blog/460981
OneOS远程升级能力实操演示 | OTA升级 OneOS远程升级能力实操演示,差分算法提升效率,OTA升级,OTA差分算法,全量升级、压缩升级、差分升级,有线网络连接,bootloader,升级平台,在线差分包制作,触摸屏以及GUI图形操作 阅读详情

相关推荐

易灵思Trion FPGA实战:5分钟搞定MIPI硬核配置(附Interface Designer避坑指南)

本文详细介绍了易灵思Trion FPGA中MIPI硬核的快速配置方法,通过Interface Designer工具实现从时钟配置到数据收发的全流程实战。特别针对初次接触Trion系列的设计师,提供了六个关键配置陷阱及解决方案,帮助开发者高效完成嵌入式视觉和高速数据传输项目。

eee77的博客 791

java 定义一个类型安全的Map

java 定义一个类型安全的Map  分享| 2013-07-06 21:24匿名 | 浏览 694 次 现在我想要这个map是严格定义的类型安全的。 使得他是严格定义的类型安全的map,map里存放的key是某个枚举(比如是EnumA)的类型,而value是这个枚举的实例对象数组。map.put(EnumA.class,EnumA.values()); 枚举EnumA.clas

evilcry2012的专栏 1324

【24届计算机校招】面经汇总

这是笔者为了能够记录一下接下来一段时间会看的一些博客视频等汇总。一是为了汇总后常回来看看,而是为了后面的人在这个阶段准备的时候有个参考。现在已经6月底了,大部分高校的夏令营都出来了,而我至今还是夏0营,前几天还为川大会收双非而感到高兴,但遗憾的是川大并没要我。而我另一位双非的朋友已经拿到offer,在决定去南开还是华师了。没有对比就没伤害,我开始怀疑自己的决策是否正确——这段时间的夏令营我都有关注,但我仅报了少部分的几个酒吧舞,而没有选择海投,我觉得只要我认准了一个东西,我拼尽全力就一定能够获取到。

JehanRio's Private Blog 804

JAVA枚举操作(获取值,转map集合)

JAVA枚举相对来说比.NET的枚举功能强大,感觉就像是一种简化版的类对象,可以有构造方法,可以重载,可以继承接口等等,但不能继承类,JAVA枚举在实际开发中应用相当频繁,以下几个封装方法在实际开发中可能用到,希望对新手有些帮助。 首先,新建一个枚举接口,为保证所有继承此接口的枚举value及description一致,便于开发使用,枚举统一接口如下。 public interface En...

qq_34412985的博客 2296

别再只画GO图了!用R语言+Perl脚本搞定单细胞KEGG富集分析与圈图(附完整代码)

本文详细介绍了如何利用R语言和Perl脚本实现单细胞KEGG富集分析全流程,从基因ID转换到专业圈图可视化。通过对比KEGG与GO分析的互补价值,提供完整的代码实现和优化技巧,帮助研究者高效完成单细胞测序数据的通路分析,并生成高质量的圈图展示结果。

weixin_28694169的博客 403

kaggle社区LLM Classification Finetuning

预测用户在一个由大型语言模型(LLMs)驱动的聊天机器人之间的正面交锋中会偏好哪些回应。获得来自聊天机器人竞技场(Chatbot Arena)的对话数据集,其中不同的LLMs对用户的提示生成答案。通过开发一个胜出的机器学习模型,帮助改进聊天机器人与人类的互动方式,并确保它们更好地符合人类的偏好。实际上是进行一个序列的分类。

m0_73542617的博客 1922

java初始化map的四种方式

第一种 最常见的方式(新建Map对象) public class Demo{ private static final Map<String, String> myMap = new HashMap<String, String>(); static{ myMap.put("a", "b"); myMap.put("c", "d"); } } 第二种 java8新特性–双括号初始化 HashMap<String

m0_54866636的博客 1033

手把手教你用开源EtherCAT协议栈SOEM开发从站设备(附避坑指南)

本文详细介绍了如何使用开源EtherCAT从站协议栈SOES进行嵌入式设备开发。内容涵盖从环境搭建、硬件准备到核心的对象字典与PDO映射配置,并重点分享了主循环集成、状态机处理以及实战调试中的常见问题与避坑指南,旨在帮助开发者高效实现设备接入工业现场总线网络。

linux的博客 629

java - 如何初始化静态Map?

import com.google.common.collect.ImmutableMap; import java.util.AbstractMap.SimpleEntry; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.stream.Collectors; import java.util.stream.Stream; /** * @Auther: 杨红杰 * .

weixin_42290901的博客 5769

如何配置SSC CANopen的excel文件

如何配置 CANOpen 的 EXCEL 文件

PI_sunyang的博客 3700

物联网与人工智能项目实战:构建智能农业监控系统

智能农业监控系统通过物联网技术实时监测农业环境中的各项数据(如土壤湿度、温度、光照强度等),并利用人工智能技术分析和预测农业生产的趋势,进而为农民提供决策支持。系统的核心目标是提高农作物的产量和品质,同时减少资源浪费,优化农田管理。环境监测:实时采集农田的温湿度、土壤湿度、光照等环境数据。作物生长监测:通过传感器和图像识别技术监测作物的生长状况。智能灌溉系统:根据土壤湿度和气候条件自动调整灌溉量。病虫害检测与预警:利用人工智能和图像识别技术自动检测作物病虫害,并及时给出预警。数据分析与决策支持。

这里只有干货:从 Modbus 通信到 MES 对接,从 YOLO 训练到工控机部署,带你搞定工业软件开发全流程。 709

[转]Java中enum的静态成员的初始化

Java中enum的静态成员的初始化 &lt;iframe id="I0_1381823279752" style="left: 0px; top: 0px; width: 106px; height: 24px; visibility: visible; position: static;" title="+1" name="I0_1381823279752" src="https:/...

iteye_5524的博客 251

【科研数据获取】根据Excel表个中的企业/公司信息批量获取经纬度坐标,并进行地图可视化

批量获取企业/公司的经纬度坐标,并进行地图可视化1 需求2 实现途径3 需求逐步实现 1 需求 在做项目的时候,老板或者项目答辩时候甲方最喜欢看到的就是可视化结果,尤其是涉及到地图可视化。很多时候会遇到企业/公司的数据,想要做地图可视化,必不可少的就是要获取它们具体地址:第一种就是像查户口一样精准一下具体的城镇街道,还有一种就是经纬度信息,前一种属于文字内容往往用于管理方面,第二种数字信息多用于数据处理与分析。 2 实现途径 获取地理信息数据,自然需要使用到地图的功能,这里直接采用百度地图的拾取坐标系统,

lys_828的博客 2337

JAVA 枚举详解

先看这一段代码: enum AccountType { SAVING, FIXED, CURRENT; private AccountType() { System.out.println(“It is a account type”); } } class EnumOne { public static void main

git_zw的博客 569

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

Kotlin enum类与companion设计的不协调--NPE导致ClassNotFoundException, Could not initialize class.

kotlin使用companion代替static, 而enum类是在类初始化时就初始化实例, 如果在enum实例中引入到了companion object中的字段, ide和编译器都不会提示错误, 但是运行那么就会导致enum类初始化失败. 因为初始化实例时, companion object还没有初始化. 引用其字段, 导致NPE, 类初始化失败, 导致ClassNotFoundException

haoyifen的专栏 3854

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

java 枚举 反序列化,Enum反序列化问题

1.Enum原理定义一个Enum,通过编译之后的字节码,我们可以发现其实现原理:public enum FruitEnum {APPLE, ORAGE}编译器是在为我们创建一个类,这个类继承自 java.lang.Enum,有两个公共的、静态的、被声明成final的属性,它们的类型就是我们定义的FruitEnum。编译器还生成了一个静态初始话器,就是字节码中static{};这一行下面的代码,其中...

weixin_33517544的博客 3651

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

enum的静态成员的初始化的问题及解决办法

enum中静态成员初始化往往会遇到问题,如下图: 上图文章地址 https://www.iteye.com/blog/rednaxelafx-460981 解决办法: 1. privatestaticMap<Integer,OperationId>map=newHashMap<Integer,OperationId>(); static{ for(OperationIdp:OperationId.values())...

三侠剑的博客 995

从零手搓kNN算法:核心原理、代码实现与实战调优

k-最近邻(kNN)是机器学习中最直观的经典算法之一,属于基于实例的懒惰学习范式。其核心原理是通过计算样本间的距离(如欧氏距离或曼哈顿距离),在特征空间中找到待预测样本的k个最近邻居,并依据这些邻居的标签进行多数投票来做出预测。这种简单而强大的机制,使其成为理解机器学习基础概念(如距离度量、超参数调优和偏差-方差权衡)的绝佳起点。在工程实践中,kNN无需显式训练模型,但预测阶段的计算复杂度较高,因此常需结合特征标准化、KD-Tree数据结构优化等技巧来提升性能。该算法在模式识别、推荐系统和分类问题中有着广泛

weixin_30423977的博客 319

java static关键字

static关键字 当我们定义一个类的时候,我们都是在描述那个类的外观与行为。没有创建任何类的对象的时候,数据存储空间没有被分配,类中的方法也不会被调用。 比如: public class robber { public void fun(){} public static void main(String args[]) { fun();//这样是编译不过的,必须得

mengxipiaoxue的专栏 441

java enum 初始化_OnJava8_初始化和清理

Java对象初始化通过构造器,清理通过垃圾收集器(Garbage Collector, GC)构造器无参构造函器,有参构造器如果创建一个类,没有自定义构造器,编译器会自动创建一个无参构造器方法重载函数名相同,参数个数或者参数类型不同,也可参数顺序不同(不推荐)this关键字this可以表示当前对象的引用,不能用于静态方法(静态方法与对象无关)public class Leaf{int i = 0;...

weixin_32307021的博客 551

enum 类的静态成员初始化在构造方法之后

Enum的初始化顺序和一般的类不同 public enum Color { RED("Red"), BLACK("Black"), WHITE("White"); private static int field = initStaticField(); Color(String name) { System.out.println("Constructo...

JasonRight 430

Java enum 枚举类的编译实现

本文探讨一下enum枚举类和普通的class有什么区别,在编译时又有什么不同。 并详细分析枚举的编译

yizishou的博客 3636

Java中如何实现单例模式

Java中,单例模式通常有2种分类饿汉模式和懒汉模式。饿汉模式指的是单例实例在类装载时就被创建了。懒汉方式值的是单例实例在首次使用时才被创建。无论是饿汉模式还是懒汉模式,都是用了一个静态成员变量来存放真正的实例。并且私有化构造函数,防止被外部实例化。单例(饿汉模式)代码:public class Singleton {     private final static Singleton INST...

weixin_34067980的博客 179

ENUM及延伸内容

1、类的初始化顺序 知识参考:http://www.cnblogs.com/jackyrong/archive/2008/08/12/1266161.html 类的加载循序为:(静态变量、静态初始化块) > (变量、初始化块) > 构造器 注意:无论是静态还...

203

枚举 enum

定义一个枚举 public class enum Status{ SUCCESS(1,"成功"), FAIL(2,"失败"); // 定义带参数的构造函数 Status(int code, String msg){ this.code = code; this.msg = msg; } // 本来定义的私有变量,...

ShuaiFanPi的博客 300
上一篇: canvas导出为图片并用JS下载
下一篇: 在本地存储localStorage/sessionStorage中保存图片和文件(真的不适合保存文件图片大数据)
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值