引言

在Java开发中,经常需要判断对象中的String属性是否为空。按照往常的写法,就是一个属性一个IF语句的去判断,这种方法在属性较少的情况下还勉强能接受,但是如果碰上很多很多属性都需要判断的时候,就会非常难受。在写了100多行的IF之后,我忍不了了,在确认并没有可以直接用的轮子之后,我决定自己来造这个轮子。程序员果然是最喜欢偷懒的一群人

想法

想要一次性判断完一个对象中所有的String属性,那么就得先知道这个对象中有哪些属性,以及这个属性的类型。
对此,我们可以利用"反射"来获取Java对象中的所有属性。

// 获取对象的 Class 对象
       Class<?> objectClass = obj.getClass();

       // 获取对象的所有属性
       Field[] fields = objectClass.getDeclaredFields();

       // 遍历属性并输出属性名、类型和值
       for (Field field : fields) {
            // 设置属性可访问,即使是私有属性
            field.setAccessible(true); 

            String fieldName = field.getName();
            Class<?> fieldType = field.getType();

            try {
                Object fieldValue = field.get(obj);
                System.out.println("属性名: " + fieldName);
                System.out.println("属性类型: " + fieldType.getName());
                System.out.println("属性值: " + fieldValue);
                System.out.println();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }

判空的语句则可以这样子,使用isEmpty也算是提高了一点点效率。

if (String.class.equals(fieldType) && StringUtils.isEmpty((String) fieldValue)) {
    System.out.println("字段:" + fieldName + " 为空。");
}

优化

这个时候似乎问题都已经解决了,但是我们仍然需要考虑一下效率问题。如果此时有多个对象需要判断,先不管这些对象是否相同,按照这种写法我们是不是每次面对一个对象的时候都要重新反射一遍去获取它的字段和属性?而反射是会影响执行速度的,所以我们应该尽量避免这种浪费资源的写法~

我在这边考虑用缓存的方法去解决这个问题,全部代码如下:

public class ObjectUtil {
    private static Map<Class<?>, Map<String, Field>> fieldCache = new HashMap<>();

    public static void haveEmpty(Object object) {
    	// 打印一下缓存
        for (Map<String, Field> classFieldCache : fieldCache.values()) {
            for (Map.Entry<String, Field> entry : classFieldCache.entrySet()) {
                String key = entry.getKey();
                Field value = entry.getValue();
                System.out.println("Key: " + key);
                System.out.println("Value: " + value);
                System.out.println();
            }
        }
        System.out.println("************小分隔**************");
        // 获取对象的 Class 属性
        Class<?> objectClass = object.getClass();

        Map<String, Field> classFieldCache = fieldCache.get(objectClass);

        // 如果缓存中不存在该类的字段信息,则进行反射获取并缓存起来
        if (classFieldCache == null) {
            classFieldCache = new HashMap<>();
            Field[] fields = objectClass.getDeclaredFields();
            for (Field field : fields) {
                // 设置属性可访问,即使是私有属性
                field.setAccessible(true);
                classFieldCache.put(field.getName(), field);
            }
            fieldCache.put(objectClass, classFieldCache);
        }

        // 遍历属性
        for(Field field : classFieldCache.values()) {
            String fieldName = field.getName();
            Class<?> fieldType = field.getType();

            try{
                Object fieldValue = field.get(object);
                /* 判空操作
                 * 使用 StringUtils 工具类或自定义的静态方法,避免直接调用 isEmpty() 方法,可以提高性能。
                 */
                if (String.class.equals(fieldType) && StringUtils.isEmpty(fieldValue)){
                    System.out.println("字段:" + fieldName+" 为空。");
                }
                System.out.println("属性名: " + fieldName);
                System.out.println("属性类型: " + fieldType.getName());
                System.out.println("属性值: " + fieldValue);
                System.out.println();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }
}

在每次执行前,先判断缓存中是否已有该对象,如果有,则直接开始,如果没有,则先反射。

测试

测试代码

User user = new User();
        user.setId(1);
        user.setUsername("");
        user.setPassword("123");
        ObjectUtil.haveEmpty(user);
        System.out.println("------------------大分隔-----------------");
        Account account = new Account();
        account.setId(1);
        account.setName("");
        account.setMoney(1.1);
        ObjectUtil.haveEmpty(account);

结果

// 第一次执行,所以缓存内没有东西。
************小分隔**************
属性名: password
属性类型: java.lang.String
属性值: 123

属性名: id
属性类型: int
属性值: 1

字段:username 为空。
属性名: username
属性类型: java.lang.String
属性值: 

------------------大分隔-----------------
// 第二次执行,打印出来上一次缓存的内容
Key: password
Value: private java.lang.String org.example.model.User.password

Key: id
Value: private int org.example.model.User.id

Key: username
Value: private java.lang.String org.example.model.User.username

************小分隔**************
属性名: money
属性类型: java.lang.Double
属性值: 1.1

字段:name 为空。
属性名: name
属性类型: java.lang.String
属性值: 

属性名: id
属性类型: java.lang.Integer
属性值: 1

最终

到这边基本上就结束啦~
最后是最终版的代码

public class ObjectUtil {
    private static Map<Class<?>, Map<String, Field>> fieldCache = new HashMap<>();

    public static Result haveEmpty(Object object) {
        // 获取对象的 Class 属性
        Class<?> objectClass = object.getClass();

        Map<String, Field> classFieldCache = fieldCache.get(objectClass);

        // 如果缓存中不存在该类的字段信息,则进行反射获取并缓存起来
        if (classFieldCache == null) {
            classFieldCache = new HashMap<>();
            Field[] fields = objectClass.getDeclaredFields();
            for (Field field : fields) {
                // 设置属性可访问,即使是私有属性
                field.setAccessible(true);
                classFieldCache.put(field.getName(), field);
            }
            fieldCache.put(objectClass, classFieldCache);
        }

        // 遍历属性
        for(Field field : classFieldCache.values()) {
            String fieldName = field.getName();
            Class<?> fieldType = field.getType();

            try{
                Object fieldValue = field.get(object);
                /* 判空操作
                 * 使用 StringUtils 工具类或自定义的静态方法,避免直接调用 isEmpty() 方法,可以提高性能。
                 */
                if (String.class.equals(fieldType) && StringUtils.isEmpty(fieldValue)){
                    return Result.error(201,"字段:" + fieldName + "为空。");
                }
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        return Result.ok("验证完毕,无空字符串");
    }
}