包装类本身可规避参数污染,因其不可变性——类final、字段private final、无setter,所有“修改”均返回新对象;需警惕AtomicInteger、Date等伪不可变类型,并确保自定义参数严格遵循不可变契约。

直接用包装类(如 Integer、String、LocalDateTime)本身就能规避参数污染,关键在于理解并守住“不可变”这个边界——不是“不改它”,而是“它根本不能被改”。
包装类的不可变性是天然防线
Java 中的 Integer、Long、Boolean、String、LocalDateTime 等,都是被设计为不可变的:类是 final 的,字段是 private final 的,没有 setter,所有“修改”操作(如 Integer + 1 或 String.concat())都返回新对象。这意味着:
- 多个线程传入同一个
Integer x = 42,谁都无法真正改变它的值 - 哪怕某个线程执行
x = x + 1,也只是让自己的局部变量x指向新对象,原42实例在堆中纹丝不动 - 作为方法参数传递时,不会出现“传进去后被别的线程悄悄改掉”的风险
警惕看似不可变、实则可变的“假包装”
有些类型名字像包装类,但行为不是不可变的,比如:
-
AtomicInteger:虽然属于java.util.concurrent.atomic,但它内部状态可变,多线程下会真实更新值,不属于本文所指的“安全参数”范畴 -
Date:非不可变,setTime()可直接篡改;应改用LocalDateTime或Instant -
StringBuilder:明显可变,即使只做参数也不安全
只要参数类型不是明确标为不可变的 JDK 类型,就默认不具备参数防污染能力。
自定义参数对象也要延续不可变契约
当业务需要传复杂参数(如订单上下文、查询条件)时,不能只靠基础包装类。必须手动构建不可变对象,且严格满足以下四点:
- 类声明为 final
- 所有字段为 private final,包括集合、时间、数组等
- 构造函数完成全部初始化,并对可变字段做防御性拷贝(如用
List.copyOf(list)或new ArrayList(src)) - getter 方法返回副本而非原始引用(例如集合类返回
unmodifiableList或再次拷贝)
示例中一个 Person 参数对象,即使外部传入的是 new ArrayList(Arrays.asList("read", "swim")),内部也只保留副本,彻底切断外部修改路径。
避免在参数链路中意外引入可变中间态
常见污染发生在“看似安全,实则转手即破”的环节:
- 把
String参数存进HashMap后,又用该 map 做缓存并被多线程共享——没问题;但如果 map 里混入了ArrayList就危险 - 用
Collections.unmodifiableList(list)包装后传参,但原始list仍在别处被修改——此时不可变包装只是“只读视图”,不等于数据本身不可变 - 将
LocalDateTime转成Date再传参,等于主动放弃不可变保障
核心原则:从参数入口到最终使用,每一步持有的引用都必须指向真正不可变的实例,中间不落地、不转换、不暴露可变容器。

















