一个枚举类的set方法引发的血案

线上集群中一台服务器的RPC接口响应成功却被误判为失败,引发连续报警。通过排查日志发现,枚举值在运行时被非法修改,最终定位到罕见的set方法调用导致问题。

事故回顾:

   线上集群环境,其中一台机器出现了诡异的现象:明明rpc接口响应的结果是成功,但是调用方判断的时候走的是失败的判断逻辑,导致持续的报警,并且只有一台机器有着个现象,先用重启大法解决了,具体原因是什么?一堆工程师开始分析。

分析原因:

   查日志,在某个时间点之后开始大量报错,大家开始围在一起讨论:

   这个时间有没有新上线?没有。

   有没有可能jar包不正确?不可能,代码已经有几天没更新了。

   甚至想到了黑客攻击?更不可能。

   再回到原来的问题上,响应结果和判断结果的逻辑都是根据枚举的值,那么看看枚举值是否正确?代码没问题。明明正确,为什    么运行时不正确?

   运行时不正确的唯一原因就是运行时被修改了,那么怎么修改呢,set方法!枚举中的set方法!恍然大悟,赶紧去看看代码有没有这个set方法,果然有,并且还有调用的地方,这个调用地方的代码平常很少被执行到,所以以前一直没有报出来,今天某个时间点只有这台机器的代码被执行了,所以 这台机器开始上演诡异的报警。

  OK,问题被找到了,修改代码,重新上线,下边分析下这个枚举类。

 枚举类:

  枚举类是jdk的一个语法糖,其本质是通过普通类实现的,只是编译器为我们进行了加工处理,每个枚举类型编译后的字节码实质都是继承自Java.lang.enum的枚举类型同名普通类,而每个枚举常量实质上是一个枚举类型同名普通类的静态常量对象,所有枚举常量都是通过静态代码块进行初始化实例赋值。

 

public enum status{
 
 start("a"),running("b"),stop();
}

我们对如上的枚举类型进行javac编译后通过Javaap -v Status.class 可以查看其编译后的字节码如下:

public final class Status extends java.lang.Enum<status>
{
public static final Status START;
public static final Status RUNNING;

public static final Status STOP;

.....


}

其实写到这里,这个问题出现的原因很清楚了,枚举本质是被编译器处理成类,枚举值是静态的常量属性,枚举只是一种语法糖,被编译器生成最终的类。从某种意义上可以说jdk1.5后引入的枚举类型是枚举常量类的代码封装而已。当用set方法进行赋值的时候,实际上是修改的一个内存中的静态变量的值,这个值原本的意义就被修改了,这时候如果其他地方再来判断,必然出错,一场血案就此发生!


   

### Java 中传递枚举类型作为方法参数的最佳实践 在 Java 编程语言中,枚举(`enum`)是一种特殊的类,用于定义一组常量。可以像其他对象一样将枚举类型的实例作为方法参数传递给函数。 #### 定义枚举类型 首先创建一个简单的 `Color` 枚举来表示颜色选项: ```java public enum Color { RED, GREEN, BLUE; } ``` #### 使用枚举作为方法参数的例子 下面展示了一个接受枚举类型的方法,并展示了如何调用它[^1]: ```java package com.example.enums; import java.util.Arrays; // 定义一个枚举类型 public enum Color { RED, GREEN, BLUE; } class EnumExample { // 接受枚举类型作为参数的方法 public static void printColor(Color color) { switch (color) { case RED: System.out.println("The selected color is Red."); break; case GREEN: System.out.println("The selected color is Green."); break; case BLUE: System.out.println("The selected color is Blue."); break; default: System.out.println("Unknown color"); } } // 主程序入口点 public static void main(String[] args) { // 调用带有枚举参数的方法 Arrays.stream(args).map(Color::valueOf).forEach(EnumExample::printColor); // 或者直接传入枚举 printColor(Color.RED); // 输出 "The selected color is Red." } } ``` 此代码片段说明了如何声明枚举以及如何编写接收该枚举作为输入参数的方法。通过这种方式可以在保持良好的可读性和安全性的同时简化代码逻辑。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值