当前位置:首页 > 后端开发 > Java反射应用:判断对象是否为NULL

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

7个月前 (05-26)27

一、背景

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

不过最后处于效率的考虑,我并没用,而是使用业务上的唯一约束做进一步的判空处理。这个工具类就送给有缘人吧,哈哈哈!
Java反射应用:判断对象是否为NULL _ Java侠

最终版工具类

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

package com.saint.javabase.reflect;

import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.util.HashSet;
import java.util.Objects;

/**
 * @author Saint
 */
public class CheckObjectIsNullUtils {
     

    /**
     * 不纳入判空逻辑的field属性
     */
    static final HashSet<String> fieldHash = new HashSet<>(4);

    static {
     
        fieldHash.add("serialVersionUID");
    }

    /**
     * 判断一个对象是否为null
     *
     * @param object
     * @return
     */
    public static boolean isNull(Object object) {
     
        // 获取object的Class对象
        Class<?> clazz = object.getClass();
        // 获取对象的所有属性
        Field[] fields = clazz.getDeclaredFields();
        // 定义返回结果
        boolean flag = true;

        for (Field field : fields) {
     
            // 使非Public类型的属性可以被访问
            field.setAccessible(true);
            Object fieldValue = null;
            Type type = null;

            try {
     
                fieldValue = field.get(object);
                // 获取到属性类型
                type = field.getType();
                // 获取属性名称
                String fieldName = field.getName();
                if (fieldHash.contains(fieldName)) {
     
                    continue;
                }

                // TODO 实际应用中建议删掉这一行,仅做测试使用
                System.out.println("属性类型:" + type + ", 属性名:" + fieldName + ", 属性值:" + fieldValue);

            } catch (Exception e) {
     
                // TODO 真实业务场景中,这里可以采用打日志替换
                e.printStackTrace();
            }

            // 只要有一个属性值不为null 就返回false 表示对象不为null
            if (fieldValue != null) {
     

                // 如果fieldValue不为null,并且fieldValue的值等于false时,则不认为对象不为空。
                if (Objects.equals(type.getTypeName(), "boolean")
                        && Objects.equals(fieldValue, false)) {
     
                    continue;
                } else {
     
                    flag = false;
                    break;
                }
            }
        }

        return flag;
    }
}

二、判空方式

判断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

package com.saint.javabase.reflect;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.List;

/**
 * @author Saint
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
     

    private  String  name;
    private Boolean age;
    private List<String> hobbies;
    private boolean student;

}

工具类:CheckObjectIsNullUtils

package com.saint.javabase.reflect;

import java.lang.reflect.Field;
import java.lang.reflect.Type;

/**
 * @author Saint
 */
public class CheckObjectIsNullUtils {
     

    /**
     * 判断一个对象是否为null
     *
     * @param object
     * @return
     */
    public static boolean isNull(Object object) {
     
        // 获取object的Class对象
        Class<?> clazz = object.getClass();
        // 获取对象的所有属性
        Field[] fields = clazz.getDeclaredFields();
        // 定义返回结果
        boolean flag = true;

        for (Field field : fields) {
     
            // 使非Public类型的属性可以被访问
            field.setAccessible(true);
            Object fieldValue = null;

            try {
     
                fieldValue = field.get(object);
                // 获取到属性类型
                Type type = field.getType();
                // 获取属性名称
                String fieldName = field.getName();

                // TODO 实际应用中建议删掉这一行,仅做测试使用
                System.out.println("属性类型:" + type + ", 属性名:" + fieldName + ", 属性值:" + fieldValue);

            } catch (Exception e) {
     
                // TODO 真实业务场景中,这里可以采用打日志替换
                e.printStackTrace();
            }

            // 只要有一个属性值不为null 就返回false 表示对象不为null
            if (fieldValue != null) {
     
                flag = false;
                break;
            }
        }

        return flag;
    }
}

1>>>>>当含有一个boolean类型的属性时

测试类:

package com.saint.javabase.reflect;

/**
 * 对象是否为空工具-测试类
 * @author Saint
 */
public class CheckObjectIsNullTest {
     
    public static void main(String[] args) {
     
        User user = new User();
        boolean flag = CheckObjectIsNullUtils.isNull(user);
        System.err.println("User对象是否为空: " +  flag);
    }
}

控制台输出:

属性类型: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

package com.saint.javabase.reflect;

import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.util.Objects;

/**
 * @author 周鑫(玖枭)
 */
public class CheckObjectIsNullUtils {
     

    /**
     * 判断一个对象是否为null
     *
     * @param object
     * @return
     */
    public static boolean isNull(Object object) {
     
        // 获取object的Class对象
        Class<?> clazz = object.getClass();
        // 获取对象的所有属性
        Field[] fields = clazz.getDeclaredFields();
        // 定义返回结果
        boolean flag = true;

        for (Field field : fields) {
     
            // 使非Public类型的属性可以被访问
            field.setAccessible(true);
            Object fieldValue = null;
            Type type = null;

            try {
     
                fieldValue = field.get(object);
                // 获取到属性类型
                type = field.getType();
                // 获取属性名称
                String fieldName = field.getName();

                // TODO 实际应用中建议删掉这一行,仅做测试使用
                System.out.println("属性类型:" + type + ", 属性名:" + fieldName + ", 属性值:" + fieldValue);

            } catch (Exception e) {
     
                // TODO 真实业务场景中,这里可以采用打日志替换
                e.printStackTrace();
            }

            // 只要有一个属性值不为null 就返回false 表示对象不为null
            if (fieldValue != null) {
     

                // 如果fieldValue不为null,并且fieldValue的值等于false时,则不认为对象不为空。
                if (Objects.equals(type.getTypeName(), "boolean")
                        && Objects.equals(fieldValue, false)) {
     
                    continue;
                } else {
     
                    flag = false;
                    break;
                }
            }
        }

        return flag;
    }
}

此时运行测试类结果如下:
Java反射应用:判断对象是否为NULL _ Java侠

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

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

User类:

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User implements Serializable {
     

    private static final long serialVersionUID = 1L;

    private  String  name;
    private Boolean age;
    private List<String> hobbies;
    private boolean student;

}

运行测试类输出如下:
Java反射应用:判断对象是否为NULL _ Java侠

结果为false,因为serialVersionUID有值。

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

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

工具类CheckObjectIsNullUtils:

public class CheckObjectIsNullUtils {
     

    /**
     * 不纳入判空逻辑的field属性
     */
    static final HashSet<String> fieldHash = new HashSet<>(4);

    static {
     
        fieldHash.add("serialVersionUID");
    }

    /**
     * 判断一个对象是否为null
     *
     * @param object
     * @return
     */
    public static boolean isNull(Object object) {
     
        // 获取object的Class对象
        Class<?> clazz = object.getClass();
        // 获取对象的所有属性
        Field[] fields = clazz.getDeclaredFields();
        // 定义返回结果
        boolean flag = true;

        for (Field field : fields) {
     
            // 使非Public类型的属性可以被访问
            field.setAccessible(true);
            Object fieldValue = null;
            Type type = null;

            try {
     
                fieldValue = field.get(object);
                // 获取到属性类型
                type = field.getType();
                // 获取属性名称
                String fieldName = field.getName();
                if (fieldHash.contains(fieldName)) {
     
                    continue;
                }

                // TODO 实际应用中建议删掉这一行,仅做测试使用
                System.out.println("属性类型:" + type + ", 属性名:" + fieldName + ", 属性值:" + fieldValue);

            } catch (Exception e) {
     
                // TODO 真实业务场景中,这里可以采用打日志替换
                e.printStackTrace();
            }

            // 只要有一个属性值不为null 就返回false 表示对象不为null
            if (fieldValue != null) {
     

                // 如果fieldValue不为null,并且fieldValue的值等于false时,则不认为对象不为空。
                if (Objects.equals(type.getTypeName(), "boolean")
                        && Objects.equals(fieldValue, false)) {
     
                    continue;
                } else {
     
                    flag = false;
                    break;
                }
            }
        }

        return flag;
    }
}

再运行测试类结果如下:
Java反射应用:判断对象是否为NULL _ Java侠
从结果来看,做判空是排除了serialVersionUID字段。

总结

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

作者:秃秃爱健身
来源链接:https://blog.csdn.net/Saintmm/article/details/120418752

“Java反射应用:判断对象是否为NULL” 的相关文章

java多线程的应用-使用两个线程打印12A34B56C78D

基本思路:使用wait/notify机制。 public class ThreadTest{ public static voi...

Java多线程看这一篇就足够了(吐血超详细总结)

Java多线程看这一篇就足够了(吐血超详细总结)

进程与线程 进程是程序的一次动态执行过程,它需要经历从代码加载,代码执行到执行完毕的一个完整的过程,这个过程也是进程本身从产生,发展到最终消亡的过程。多进程操作系统能同...

Java Socket实现基于TCP和UDP多线程通信

Java Socket实现基于TCP和UDP多线程通信

一.通过Socket实现TCP编程 1.1 TCP编程   TCP协议是面向连接,可靠的,有序的,以字节流的方式发送数据。基于TCP协议实现网络通信的类有客户端的Socket...

Android Gradle plugin requires Java 11 问题解决

Android Gradle plugin requires Java 11 问题解决

AGP 7.0.0-alpha02起需要使用Java 11 https://developers-jp.googleblog.com/2020/12...

java基础集合简介Map(三)下

java基础集合简介Map(三)下

  --Map接口简介   今天来看一看map集合,map映射接口,用于存放键值对,<key,value>,通过key来查找value,顾名思义...

详细分析 Java 中实现多线程的方法有几种?(从本质上出发)

详细分析 Java 中实现多线程的方法有几种?(从本质上出发)

目录 详细分析 Java 中实现多线程的方法有几种?(从本质上出发) 正确的说法(从本质上出发) 经典...

Java集合详解(非常详细!!!)

Java集合详解(非常详细!!!)

前言 数据结构作为每一个开发者不可回避的问题,而 Java 对于不同的数据结构提供了非常成熟的实现,这一个又一个实现既是面试中的难点,也是工作中必不可少的工具,在此,笔...

JAVA多线程下高并发的处理经验

JAVA多线程下高并发的处理经验

java中的线程:java中,每个线程都有一个调用栈存放在线程栈之中,一个java应用总是从main()函数开始运行,被称为主线程。一旦创建一个新的线程,就会产生一个线程栈。...

Java -version与配置的Path环境变量不一致

Java -version与配置的Path环境变量不一致 问题描述:     设置了环境变量JAVA_HOME为jdk1.5.0_14的安装...

java多线程中的三种特性

java多线程中的三种特性 原子性(Atomicity) 原子性是指在一个操作中就是cpu不可以在中途暂停然后再调度,既不被中断操作,要不执行完成,要不就不执行。 如果...