反射读取字段为null需先调用setaccessible(true),基本类型返回默认值,引用类型未初始化才为null;注解需加@retention(retentionpolicy.runtime);beanutils不安全,建议手写field赋值器。

Java 反射读取字段值时 null 怎么办
字段值为 null 不一定是没赋值,大概率是反射没设权限或对象本身字段就是空。Java 默认禁止访问私有成员,Field.get(obj) 遇到私有字段会直接抛 IllegalAccessException,而不是返回 null——真返回 null,说明字段类型是引用类型且确实未初始化。
- 必须在
Field.setAccessible(true)后再调用get(),否则私有字段读不到 - 如果字段是基本类型(如
int),反射返回的是其包装类(Integer),但未赋值时不会是null,而是默认值(0);只有包装类字段才可能为null - 注意静态字段:传
null给Field.get()是合法的,但实例字段必须传真实对象
@Column 注解没生效?检查 ClassLoader 和运行时保留策略
自定义注解如 @Column(name = "user_name") 在反射中取不到,八成是注解没加 @Retention(RetentionPolicy.RUNTIME)。编译期注解(SOURCE)或类加载期注解(CLASS)在运行时根本不可见,反射查不到任何东西。
- 确认注解定义里有
@Retention(RetentionPolicy.RUNTIME) - 确认字段上真写了注解,且没有被 IDE 或 Lombok 的
@Data等自动覆盖掉(Lombok 生成的 getter/setter 不影响字段注解,但字段本身可能被删) - 如果用模块化(Java 9+),还要检查
module-info.java是否导出了含注解的包
用 BeanUtils.copyProperties 做映射安全吗
不安全,尤其在 ORM 场景下。Apache Commons BeanUtils.copyProperties 依赖 getter/setter,且默认忽略类型不匹配、静默跳过异常,字段名相同但类型不同(比如数据库存的是 String 时间,实体用 LocalDateTime)会导致赋值失败或数据截断,还查不出错。
- 它不处理
ResultSet到对象的列名映射,也不支持自定义转换逻辑(如把"Y/N"转布尔) - 性能差:每次 copy 都要反射查找所有 setter,且内部用
PropertyDescriptor缓存不完善 - 替代方案更可控:手写一个基于
Field+setAccessible的赋值器,配合白名单字段和显式类型转换
为什么 ResultSet.getObject(i) 返回 Object 而不是具体类型
因为 JDBC 驱动不知道你字段对应哪个 Java 类型,它只按 SQL 类型返回最接近的默认映射(比如 VARCHAR → String,INTEGER → Integer)。但如果你的实体字段是 long,而数据库是 BIGINT,getObject() 返回 Long 没问题;可如果是 DECIMAL,它可能返回 BigDecimal,强制转 Double 就丢精度。
- 别用
getString()或getInt()强转,容易抛SQLException或截断(比如getInt()对NULL返回0) - 优先用
getObject(int, Class)(JDBC 4.2+),例如rs.getObject(1, LocalDateTime.class),驱动会尝试转换 - 对不确定类型(如 JSON 字段),统一收为
String再交给 Jackson/Gson 解析,比硬转更稳
真正难的不是反射调用,而是字段类型、空值语义、数据库类型映射这三者的对齐。漏掉任意一环,运行时就可能突然崩在某个冷门字段上。










