Java反射应用:判断对象是否为NULL

落日映苍穹つ 2023-10-02 10:07 242阅读 0赞

一、背景

最近在做对接美国的EasyPost快递平台时,发现使用Objects.isNull()判断EasyPost返回的序列化之后的空实体(JSON体为:{})时,返回结果并不是false;然后情不自禁就自己写了个使用反射判断Java对象是否为“NULL”的工具类。

不过最后处于效率的考虑,我并没用,而是使用业务上的唯一约束做进一步的判空处理。这个工具类就送给有缘人吧,哈哈哈!
在这里插入图片描述

最终版工具类

只想知道最终工具类的老哥,请copy如下代码:

  1. package com.saint.javabase.reflect;
  2. import java.lang.reflect.Field;
  3. import java.lang.reflect.Type;
  4. import java.util.HashSet;
  5. import java.util.Objects;
  6. /**
  7. * @author Saint
  8. */
  9. public class CheckObjectIsNullUtils {
  10. /**
  11. * 不纳入判空逻辑的field属性
  12. */
  13. static final HashSet<String> fieldHash = new HashSet<>(4);
  14. static {
  15. fieldHash.add("serialVersionUID");
  16. }
  17. /**
  18. * 判断一个对象是否为null
  19. *
  20. * @param object
  21. * @return
  22. */
  23. public static boolean isNull(Object object) {
  24. // 获取object的Class对象
  25. Class<?> clazz = object.getClass();
  26. // 获取对象的所有属性
  27. Field[] fields = clazz.getDeclaredFields();
  28. // 定义返回结果
  29. boolean flag = true;
  30. for (Field field : fields) {
  31. // 使非Public类型的属性可以被访问
  32. field.setAccessible(true);
  33. Object fieldValue = null;
  34. Type type = null;
  35. try {
  36. fieldValue = field.get(object);
  37. // 获取到属性类型
  38. type = field.getType();
  39. // 获取属性名称
  40. String fieldName = field.getName();
  41. if (fieldHash.contains(fieldName)) {
  42. continue;
  43. }
  44. // TODO 实际应用中建议删掉这一行,仅做测试使用
  45. System.out.println("属性类型:" + type + ", 属性名:" + fieldName + ", 属性值:" + fieldValue);
  46. } catch (Exception e) {
  47. // TODO 真实业务场景中,这里可以采用打日志替换
  48. e.printStackTrace();
  49. }
  50. // 只要有一个属性值不为null 就返回false 表示对象不为null
  51. if (fieldValue != null) {
  52. // 如果fieldValue不为null,并且fieldValue的值等于false时,则不认为对象不为空。
  53. if (Objects.equals(type.getTypeName(), "boolean")
  54. && Objects.equals(fieldValue, false)) {
  55. continue;
  56. } else {
  57. flag = false;
  58. break;
  59. }
  60. }
  61. }
  62. return flag;
  63. }
  64. }

二、判空方式

判断Java对象是否为null可以有两层含义:

(1)第一层: 直接使用 object == null 去判断,对象为null的时候返回true,不为null的时候返回false。

(2)第二层:在object != null 为true的情况 下,进一步去判断对象的所有属性是否为null

1)第一层判空

建议使用上图的Objects.isNull()方法,比自己写null ===xxx更加优雅。当然这里所做的是只是对象是否null,如果对象被new出来了,但是它的所有属性都是默认值null,Objects.isNull()方法是判断不出来的。

尤其是在前后端交互的过程中,前端传过来的空对象实际是:{},这时对象不为null,但是对象的所有属性为null。

2)使用反射做第二层判空

User.class

  1. package com.saint.javabase.reflect;
  2. import lombok.AllArgsConstructor;
  3. import lombok.Data;
  4. import lombok.NoArgsConstructor;
  5. import java.util.List;
  6. /**
  7. * @author Saint
  8. */
  9. @Data
  10. @AllArgsConstructor
  11. @NoArgsConstructor
  12. public class User {
  13. private String name;
  14. private Boolean age;
  15. private List<String> hobbies;
  16. private boolean student;
  17. }

工具类:CheckObjectIsNullUtils

  1. package com.saint.javabase.reflect;
  2. import java.lang.reflect.Field;
  3. import java.lang.reflect.Type;
  4. /**
  5. * @author Saint
  6. */
  7. public class CheckObjectIsNullUtils {
  8. /**
  9. * 判断一个对象是否为null
  10. *
  11. * @param object
  12. * @return
  13. */
  14. public static boolean isNull(Object object) {
  15. // 获取object的Class对象
  16. Class<?> clazz = object.getClass();
  17. // 获取对象的所有属性
  18. Field[] fields = clazz.getDeclaredFields();
  19. // 定义返回结果
  20. boolean flag = true;
  21. for (Field field : fields) {
  22. // 使非Public类型的属性可以被访问
  23. field.setAccessible(true);
  24. Object fieldValue = null;
  25. try {
  26. fieldValue = field.get(object);
  27. // 获取到属性类型
  28. Type type = field.getType();
  29. // 获取属性名称
  30. String fieldName = field.getName();
  31. // TODO 实际应用中建议删掉这一行,仅做测试使用
  32. System.out.println("属性类型:" + type + ", 属性名:" + fieldName + ", 属性值:" + fieldValue);
  33. } catch (Exception e) {
  34. // TODO 真实业务场景中,这里可以采用打日志替换
  35. e.printStackTrace();
  36. }
  37. // 只要有一个属性值不为null 就返回false 表示对象不为null
  38. if (fieldValue != null) {
  39. flag = false;
  40. break;
  41. }
  42. }
  43. return flag;
  44. }
  45. }
1>>>>>当含有一个boolean类型的属性时

测试类:

  1. package com.saint.javabase.reflect;
  2. /**
  3. * 对象是否为空工具-测试类
  4. * @author Saint
  5. */
  6. public class CheckObjectIsNullTest {
  7. public static void main(String[] args) {
  8. User user = new User();
  9. boolean flag = CheckObjectIsNullUtils.isNull(user);
  10. System.err.println("User对象是否为空: " + flag);
  11. }
  12. }

控制台输出:

属性类型:class java.lang.String, 属性名:name, 属性值:null
属性类型:class java.lang.Boolean, 属性名:age, 属性值:null
属性类型:interface java.util.List, 属性名:hobbies, 属性值:null
属性类型:boolean, 属性名:student, 属性值:false
User对象是否为空: false

从输出上来看,收到boolean类型的影响,结果为false。

另外需要注意的是数据类型: boolean与Boolean
boolean 定义的变量默认值为false,Boolean定义的变量默认值为 null.

ps: 在定义boolean类型变量时,最好不要使用 isXxx,因为默认生成的get方法就是 isXxx(), RPC框架在反向解析的时候,以为对应的属性名是 xxx,从而导致属性获取不到,抛出异常。

2>>>>>修改工具类,兼容boolean类型的属性

工具类:CheckObjectIsNullUtils

  1. package com.saint.javabase.reflect;
  2. import java.lang.reflect.Field;
  3. import java.lang.reflect.Type;
  4. import java.util.Objects;
  5. /**
  6. * @author 周鑫(玖枭)
  7. */
  8. public class CheckObjectIsNullUtils {
  9. /**
  10. * 判断一个对象是否为null
  11. *
  12. * @param object
  13. * @return
  14. */
  15. public static boolean isNull(Object object) {
  16. // 获取object的Class对象
  17. Class<?> clazz = object.getClass();
  18. // 获取对象的所有属性
  19. Field[] fields = clazz.getDeclaredFields();
  20. // 定义返回结果
  21. boolean flag = true;
  22. for (Field field : fields) {
  23. // 使非Public类型的属性可以被访问
  24. field.setAccessible(true);
  25. Object fieldValue = null;
  26. Type type = null;
  27. try {
  28. fieldValue = field.get(object);
  29. // 获取到属性类型
  30. type = field.getType();
  31. // 获取属性名称
  32. String fieldName = field.getName();
  33. // TODO 实际应用中建议删掉这一行,仅做测试使用
  34. System.out.println("属性类型:" + type + ", 属性名:" + fieldName + ", 属性值:" + fieldValue);
  35. } catch (Exception e) {
  36. // TODO 真实业务场景中,这里可以采用打日志替换
  37. e.printStackTrace();
  38. }
  39. // 只要有一个属性值不为null 就返回false 表示对象不为null
  40. if (fieldValue != null) {
  41. // 如果fieldValue不为null,并且fieldValue的值等于false时,则不认为对象不为空。
  42. if (Objects.equals(type.getTypeName(), "boolean")
  43. && Objects.equals(fieldValue, false)) {
  44. continue;
  45. } else {
  46. flag = false;
  47. break;
  48. }
  49. }
  50. }
  51. return flag;
  52. }
  53. }

此时运行测试类结果如下:
在这里插入图片描述

从结果上来看,已经符合我们的预期了。

3>>>>>当User类实现序列化接口 Serializable 时

User类:

  1. @Data
  2. @AllArgsConstructor
  3. @NoArgsConstructor
  4. public class User implements Serializable {
  5. private static final long serialVersionUID = 1L;
  6. private String name;
  7. private Boolean age;
  8. private List<String> hobbies;
  9. private boolean student;
  10. }

运行测试类输出如下:
在这里插入图片描述

结果为false,因为serialVersionUID有值。

所以我们在判空时需要排除一些特殊的字段。

4>>>>>修改工具类,兼容特殊字段serialVersionUID

工具类CheckObjectIsNullUtils:

  1. public class CheckObjectIsNullUtils {
  2. /**
  3. * 不纳入判空逻辑的field属性
  4. */
  5. static final HashSet<String> fieldHash = new HashSet<>(4);
  6. static {
  7. fieldHash.add("serialVersionUID");
  8. }
  9. /**
  10. * 判断一个对象是否为null
  11. *
  12. * @param object
  13. * @return
  14. */
  15. public static boolean isNull(Object object) {
  16. // 获取object的Class对象
  17. Class<?> clazz = object.getClass();
  18. // 获取对象的所有属性
  19. Field[] fields = clazz.getDeclaredFields();
  20. // 定义返回结果
  21. boolean flag = true;
  22. for (Field field : fields) {
  23. // 使非Public类型的属性可以被访问
  24. field.setAccessible(true);
  25. Object fieldValue = null;
  26. Type type = null;
  27. try {
  28. fieldValue = field.get(object);
  29. // 获取到属性类型
  30. type = field.getType();
  31. // 获取属性名称
  32. String fieldName = field.getName();
  33. if (fieldHash.contains(fieldName)) {
  34. continue;
  35. }
  36. // TODO 实际应用中建议删掉这一行,仅做测试使用
  37. System.out.println("属性类型:" + type + ", 属性名:" + fieldName + ", 属性值:" + fieldValue);
  38. } catch (Exception e) {
  39. // TODO 真实业务场景中,这里可以采用打日志替换
  40. e.printStackTrace();
  41. }
  42. // 只要有一个属性值不为null 就返回false 表示对象不为null
  43. if (fieldValue != null) {
  44. // 如果fieldValue不为null,并且fieldValue的值等于false时,则不认为对象不为空。
  45. if (Objects.equals(type.getTypeName(), "boolean")
  46. && Objects.equals(fieldValue, false)) {
  47. continue;
  48. } else {
  49. flag = false;
  50. break;
  51. }
  52. }
  53. }
  54. return flag;
  55. }
  56. }

再运行测试类结果如下:
在这里插入图片描述
从结果来看,做判空是排除了serialVersionUID字段。

总结

综上所述,当你需要判断Java对象是否为null的时候,你可以先通过 obj == null 去判断,如果obj 不等于null,再根据业务需求决定是否需要进一步判断 obj的所有属性是否都为null。

发表评论

表情:
评论列表 (有 0 条评论,242人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Java判断对象是否

    在Java编程中,经常需要判断一个对象是否为空。一个对象为空表示它没有被实例化,或者它的引用值为null。在这篇文章中,我们将详细介绍如何判断一个对象是否为空,并提供相应的源代

    相关 JAVA 反射判断NULL

    用户完善资料,会进行修改状态, 我本来是在用户添加资料那进行同步操作状态,但是产品经理说,用户可以选择不填满这个档案资料,好吧,只能 改需求30个字段,不可以一个一个去判断是