
本文揭示了 Tetris 游戏开发中一个典型陷阱:使用数组引用赋值(如 arr[i] = arr[i-1])替代逐元素拷贝,导致多个 Block 实例共享同一引用,进而引发 setFilled(true) 意外修改非目标格子的状态。
本文揭示了 tetris 游戏开发中一个典型陷阱:使用数组引用赋值(如 `arr[i] = arr[i-1]`)替代逐元素拷贝,导致多个 block 实例共享同一引用,进而引发 `setfilled(true)` 意外修改非目标格子的状态。
在 Java 的二维数组操作中,Block[][] field 的每一行(即 field[y])本质上是一个 Block[] 类型的对象引用。当执行如下代码时:
field[y + fieldExtra] = field[y + fieldExtra - 1];
你并非在“复制”第 y+fieldExtra−1 行中每个 Block 的状态,而是将整行数组对象的引用地址直接赋给了上一行——这意味着 field[y + fieldExtra] 和 field[y + fieldExtra - 1] 指向完全相同的 Block[] 实例。更严重的是,如果该行内多个 Block 对象本身也存在重复引用(例如通过浅拷贝或静态复用),则一次 setFilled(true) 调用可能同步影响多个逻辑上独立的格子,表现为“方块向上拉伸”“状态溢出”等诡异现象。
这正是原问题中“只填充到索引 19 后停止”的原因:fieldExtra = 20,而 bug 触发于清除满行时对 field[20]、field[21] 等高索引行执行了引用覆盖,使得顶部若干行(如 field[19] 到 field[25])实际指向同一块内存区域;一旦其中任一 Block 状态变更,所有共享该引用的行都会体现相同变化。
✅ 正确做法是深拷贝状态,而非复制引用。应逐列遍历并显式同步每个 Block 的 filled 属性:
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
// ✅ 安全:仅复制状态值,不共享对象引用 for (int i = 0; i <p>⚠️ 进阶注意事项:</p>
- 若
Block类包含其他可变字段(如颜色、类型标识),需确保所有关键状态均被显式复制; - 更健壮的设计可考虑
Block为不可变类(immutable),用新实例替代修改,彻底规避状态污染; - 在调试类似问题时,可在关键节点打印
System.identityHashCode(block)验证是否为同一对象; - 使用
Arrays.deepEquals()或单元测试验证清除逻辑前后各行内容的独立性。
该案例深刻说明:在涉及对象数组的操作中,“引用即共享”,任何省略逐元素处理的“快捷赋值”都可能埋下隐蔽的并发式状态污染隐患。严谨的数组操作,永远始于对引用语义的清醒认知。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










