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

一、背景

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

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

最终版工具类

只想知道最终工具类的老哥,请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;
    }
}

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

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

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;

}

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

结果为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;
    }
}

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

总结

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

秃秃爱健身

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值