for (Color c : Color.values()) 这种写法几乎每个 Java 项目里都能找到,但很少有人真的验证过:values() 每次调用到底是不是真的重新创建了一个数组。
上一篇:LongAdder 比 AtomicLong 快多少:16 线程下真实测出 40 倍以上
enum 类型的 values() 方法是编译器自动生成的,内部原理是克隆一份存着所有枚举实例的私有静态数组。
两次 values() 调用返回的数组 == 比较(是否同一个实例) = false 两次调用返回的数组内容 equals(Arrays.equals) = true 修改一次 values() 返回的数组后,下次 values()[0] = RED(预期还是 RED,不受影响)
三行真实结果拼出完整的行为:两次调用拿到的是不同的数组实例(== 为 false),但内容完全相同(equals 为 true)——说明每次调用都真实创建了一份新的、内容一致的克隆。第三行更直接验证了"为什么要克隆":如果不克隆、直接把内部私有数组暴露出去,调用方改一下返回值(比如 values()[0] = BLACK)就能真实污染所有枚举类的内部状态;真实测出改了这次拿到的数组之后,下次调用 values() 结果完全不受影响,说明克隆确实起到了隔离作用。
克隆一个只有 6 个元素的小数组,单次开销很小,但如果这行代码在一个执行 2000 万次的循环里,真实差距会被放大到能测出来。
每次调用 values() 真实耗时 = 159ms (sum=300000000) 缓存后复用同一个数组耗时 = 79ms (sum=300000000) 倍数 = 2.0
第二次独立运行倍数是 1.9,结果稳定在 2 倍左右。两组的求和结果完全一致(sum=300000000),证明计算内容真实等价,差距纯粹来自 values() 每次调用都要真实分配一个新数组并把内容拷贝进去这一步。
每一行都是真实运行这段代码得到的结果。