必须通过文件流读取数值后调用 Integer.valueOf() 才能真实触发缓存判断;反射验证 IntegerCache.high/low 及数组长度;多线程测试 identityHashCode 稳定性;结合 -XX:AutoBoxCacheMax 参数与 jstat/PrintClassHistogram 验证扩容效果。

测试包装类缓存边界不能只靠几个字面量写法,必须切断编译期优化、真实触发运行时装箱路径,才能暴露底层行为。核心是让 JVM 走 valueOf() 的标准逻辑,而不是被常量折叠或内联掩盖。
用文件流读取 + valueOf() 强制走缓存判断
这是最可靠的方式——它把数值从外部输入,完全绕过编译器对字面量的优化:
- 准备一个文本文件(如
test-values.txt),每行一个整数,覆盖关键点:-129、-128、0、127、128、200 - 用
BufferedReader逐行读取字符串,再调用Integer.parseInt()转成int,最后明确调用Integer.valueOf(i) - 禁止使用
Integer a = 127这类自动装箱写法,也禁用new Integer(127) - 将每次生成的对象存入
List,再做两两==和.equals()比较
用反射验证 IntegerCache 实际范围
不依赖文档,直接查 JVM 当前生效的缓存上下限:
- 通过反射获取
IntegerCache.high和IntegerCache.low的值(注意需先触发IntegerCache初始化) - 检查
IntegerCache.cache数组长度是否等于high - low + 1 - 验证
Integer.valueOf(low)和Integer.valueOf(high)是否返回数组中同一位置对象 - 对比
Integer.valueOf(low - 1)和Integer.valueOf(high + 1),确认它们不复用缓存对象(==为false)
跨线程+多次加载验证复用稳定性
缓存是静态共享的,但初始化时机和并发访问可能影响观测结果:
- 在多个线程中分别调用
Integer.valueOf(127)十次,收集所有返回对象,用System.identityHashCode()检查是否全部一致 - 对
128做同样操作,确认每次得到的 identity hash 都不同 - 启动应用后延迟几秒再执行测试,确保
IntegerCache已完成初始化(避免首次调用触发同步块干扰)
结合 JVM 参数验证扩容效果
若生产环境设置了 -XX:AutoBoxCacheMax=5000,必须验证它真正生效:
- 启动时添加该参数,再运行上述文件流测试,重点检查
5000和5001的==行为差异 - 用
jstat -gc观察 Eden 区对象分配速率:缓存命中率高时,Integer分配应显著减少 - 配合
-XX:+PrintClassHistogram在测试前后各 dump 一次,比对java.lang.Integer实例数变化
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











