Java中必须加null检查的三类位置是:方法入参、外部传入的集合/数组、返回值为Optional以外的引用类型;应优先使用Objects.requireNonNull(),构造函数参数必须检查,Map/List获取后需判空,toString()/equals()中避免隐式null调用。

Java里哪些地方必须加null检查?
不是所有对象都要判空,但三类位置不加就容易在运行时崩:方法入参、外部传入的集合/数组、返回值为Optional以外的引用类型。JDK 8+ 的Objects.requireNonNull()比手写if (x == null)更明确,且能附带提示信息。
- 构造函数参数必须检查——否则对象一创建就处于非法状态
-
Map.get()或List.get()后直接调用方法?先确认是否为null,尤其当键/索引来自用户输入或配置时 - 避免在
toString()、equals()里隐式触发null引用——这些方法可能被日志、断言、框架自动调用,出错堆栈难定位
用final字段和不可变类真能防bug?
能,但只在「声明即赋值 + 不暴露可变内部状态」的前提下成立。常见误用是把final List当成不可变集合——其实只是引用不可变,内容仍可被add()或clear()修改。
- 用
Collections.unmodifiableList()或ImmutableList.of()(Guava)替代final ArrayList - 自定义不可变类时,所有字段必须
final,且不提供 setter;若含数组或集合,需在构造时深拷贝或包装为不可变视图 - getter 方法返回集合时,别直接返回私有字段,要返回
Collections.unmodifiableXxx()或新副本
try-catch吞掉异常为什么比崩溃还危险?
因为掩盖了本该失败的场景,让错误在下游以更诡异的方式浮现,比如数据错乱、状态不一致、重试逻辑失效。防御性编程不是「兜底容错」,而是「清晰表达契约边界」。
- 不要捕获
Exception或Throwable然后log.info()完事——至少要重新抛出或转为更明确的业务异常 - 资源关闭务必用
try-with-resources,而不是在catch里手动close()——后者在异常链中可能被跳过 - 对
NumberFormatException这类可预期异常,优先用预校验(如正则或Integer.parseInt()前先String.matches("\\d+")),而非依赖catch兜底
Spring Controller里怎么写才算防御性?
Web 层是外部输入第一道闸门,这里松懈,后端再严也没用。重点不在“拦住所有非法请求”,而在于「让错误反馈足够早、足够准」。
立即学习“Java免费学习笔记(深入)”;
- 用
@Valid配合@NotNull、@Size等注解做参数校验,失败直接返回400 Bad Request,别等进 service 才抛IllegalArgumentException - 路径变量(
@PathVariable)和查询参数(@RequestParam)默认允许null,若业务不允许,显式写required = true - 别在 controller 里直接调用
repository.save(entity)——entity 可能含未校验字段,应先走 DTO → VO 转换,并在 service 层做领域规则检查
防御性编程最难的不是写检查语句,是判断「这个检查该放在哪一层」以及「检查失败后该让谁来负责修复」。很多人把校验全堆在 DAO 层,结果前端传错格式,后端报ConstraintViolationException,连哪个字段错了都看不出来。











