String的非空判断、Integer的非空判断、list的大小判断,对象的非空判断

本文介绍了几种常用的非空判断方法,包括String、Integer、List及一般对象的判断方式。通过这些简单而实用的技巧,可以帮助开发者有效避免空指针异常等问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、String的非空判断。

StringUtils.isNotEmpty(String str);

2、Integer的非空判断。

null != Integer ;

3、list的大小判断。

list.size() == 0

4、对象的非空判断

null != object

<think>根据用户需求,我们需要一个Java工具类,用于判断对象及其所有属性是否为。这里需要考虑: 1. 对象本身是否为null 2. 如果对象是集合、数组、Map等,是否为 3. 如果对象是普通JavaBean,检查其所有属性(包括嵌套对象)是否为 注意:由于Java反射机制可能会访问私有属性,我们需要设置可访问性。同时,为了避免无限递归(如循环引用),我们需要一个已访问对象的集合。 另外,我们还需要考虑一些特殊类型,如基本类型(它们不可能为null,但我们可以检查其默认值是否等于当前值?但用户需求是“为”,通常对于基本类型我们只检查是否为默认值(如0、false等)?但用户需求可能不同,这里我们按照常规做法:基本类型的包装类按对象处理,基本类型本身如果为0/false等视为。但注意,用户可能需要将基本类型的默认值视为,所以我们可以提供选项,但为了简单,我们先按常规:基本类型永远不为(因为总有一个值),但用户可能希望检查基本类型是否为默认值。因此,我们需要明确需求。 根据常见工具类,我们通常这样处理: - 基本数据类型:由于不能为null,所以不为。但有时我们需要检查它们是否为默认值(0, false等),我们可以添加一个选项来控制是否检查基本类型的默认值。 - 包装类型:为null视为。 - 数组:检查长度是否为0。 - 集合(Collection):检查是否为集合。 - Map:检查是否为Map。 - 字符串:检查是否为null或字符串(trim后可选)。 - 其他对象:递归检查其属性。 由于需求复杂,我们设计一个工具类,提供多个静态方法,并允许通过选项配置。 但是,用户要求是“判断对象及其属性是否都为”,所以我们设计一个入口方法:`isAllNull(Object obj)`,它递归检查所有属性。 然而,递归检查所有属性可能性能开销大,且可能遇到循环引用。因此,我们需要一个`Set`来记录已经访问过的对象(避免循环引用导致的无限递归)。 步骤: 1. 如果对象为null,返回true。 2. 如果对象已经被访问过(在当前的检查路径中),则跳过,返回false(因为循环引用说明有对象在引用链中,但这里我们为了避免循环,直接认为不为?或者我们可以认为循环引用部分已经检查过,如果当前对象已经出现在检查路径中,说明我们正在递归检查它的属性,此时我们不应该再次检查它,而是跳过,并认为它不为?但这样可能不正确。实际上,循环引用存在时,我们无法简单地判断整个对象图为。因此,我们采用:如果对象已经在当前检查路径中,则跳过该对象(即不再检查其内部),并返回false(表示)。这是一种保守策略:只要出现循环引用,我们就不认为整个对象,因为循环引用的存在意味着至少有两个对象相互引用,那么它们都不为(因为彼此引用)。但实际上,它们可能除了这个循环引用外没有其他属性,但我们无法确定。所以,我们保守地认为循环引用的对象。 3. 根据对象类型分别处理: - 基本类型(包括包装类):如果是基本类型,则不为(因为基本类型有值)。但如果是包装类,为null才为。注意:用户可能希望将基本类型的默认值视为,我们可以提供一个选项,但为了简化,我们不在基础方法中处理,而是提供重载方法。 - 数组:检查长度,如果长度为0,则返回true,否则递归检查每个元素?但是,用户可能希望数组中没有元素才认为整个数组为?但通常,数组为是指数组长度为0。而用户要求的是“对象及其属性为”,对于数组,我们通常只检查长度。但为了严格,我们可以设计为:如果数组长度为0,则;否则,检查数组的每个元素是否都为(递归)。同样,集合和Map也类似处理。 - 集合:先检查集合是否为(size==0),如果不为,则遍历每个元素,如果有一个元素,则返回false。注意:这里我们递归检查每个元素是否为(即元素对象及其属性是否为)?但这样可能太严格,因为用户可能只关心集合本身是否为(没有元素),而不关心元素内容。所以,我们需要明确:工具类的目的是判断对象本身以及其所有属性(包括嵌套对象的属性)是否为。因此,对于集合,我们不仅要求集合本身(有元素),而且要求每个元素都是(即每个元素都是null或对象)?这不太符合常规。通常,集合不为就是有元素,即使元素是对象,集合本身也不为。所以,这里我们需要权衡。 重新定义“”的含义: - 对象对象为null,或者对象的所有属性都为(对于JavaBean),或者对于集合/数组等,集合/数组中没有元素(即size=0)?还是说对于集合,要求集合本身不为,但其中的每个元素都是对象?后者显然要求更高。 用户的需求是“对象及其属性是否都为”,所以: - 对于数组:要求数组长度为0,或者数组中的每个元素都是(递归判断)?但这样递归下去,如果数组中有元素,则整个数组。 - 对于集合:同样,要求集合为(size=0)或者集合中的每个元素都是(递归判断)?但这样,一个包含一个对象的集合,会被认为是的(因为集合不为),即使这个对象内部什么都没有。 所以,我们有两种理解: 1. 严格递归:对象当且仅当: - 对象为null,或者 - 对象是数组,且数组长度0,或者数组中的所有元素都为(递归) - 对象是集合,且集合为(size=0),或者集合中的所有元素都为(递归) - 对象是Map,且Map为,或者Map中的所有值都为(递归) - 对象是字符串,且字符串为(trim后可选) - 其他对象:所有字段(包括父类字段)都为(递归) 2. 严格:只检查对象本身的状态(如数组长度、集合大小),而不递归检查元素内容。 用户要求的是“对象及其属性”,所以应该是严格递归的。 但是,递归检查元素内容可能会导致性能问题,而且对于大集合不合适。因此,我们可以提供一个开关,是否递归检查集合/数组的元素。 由于需求复杂,我们设计一个工具类,提供多个静态方法,并允许配置。 为了简单,我们先实现一个基本版本,只递归检查JavaBean的属性,对于集合、数组、Map等,我们只检查它们本身是否为(即集合是否size=0,数组长度=0,Map是否size=0),而不递归检查其内部元素。因为递归检查内部元素可能会导致意想不到的结果,而且通常用户只关心容器是否为(没有元素),而不关心容器内的元素是否为对象。 但是,用户要求是“对象及其属性”,如果属性是一个集合,那么集合本身不为(有元素)就说明这个属性不为,因此整个对象就不为。所以,我们不需要检查集合内的元素是否为,因为集合已经有元素了,所以这个属性就不为,整个对象就不为。因此,我们只需要检查集合是否为(size=0)即可。 同理,数组也是如此。 因此,我们的设计: 1. 对象为null -> 2. 对象是数组 -> 检查数组长度是否为0 3. 对象是集合(Collection) -> 检查isEmpty() 4. 对象是Map -> 检查isEmpty() 5. 对象是字符串 -> 检查是否为null或字符串(trim可选,默认不trim) 6. 其他对象(JavaBean)-> 递归检查所有字段(包括父类的字段)是否为(使用反射,并处理循环引用) 对于基本数据类型,我们不考虑,因为基本数据类型的字段不可能为null,它们有默认值,所以永远不为(除用户希望检查是否为默认值,但我们这里不处理,因为需求是“为”,基本数据类型不为null)。 但是,包装类型可以为null,所以需要处理。 所以,步骤: - 先判断对象是否为null,是则返回true。 - 检查对象类型,如果是数组、集合、Map、字符串,则按上述规则判断。 - 如果是其他类型,获取其所有字段(包括父类),递归检查每个字段的值是否为(跳过静态字段和常量)。 注意:为了避免循环引用,我们使用一个IdentityHashMap(或者Set)来记录已经访问过的对象。 实现: 由于反射可能抛出异常,我们需要处理。 另外,对于某些字段,我们可能需要跳过,比如static、transient等,但用户可能希望检查所有字段,所以我们可以默认跳过static和transient字段?或者提供选项。我们先跳过static字段,因为static字段属于类,不属于对象。transient字段通常不参与序列化,但用户可能希望检查,所以我们先不跳过transient。 我们写一个工具类`ObjectEmptyChecker`,提供静态方法: - `public static boolean isEmpty(Object obj)`: 入口方法,不递归检查集合内部元素,只检查容器本身是否为(无元素)。 - `public static boolean isEmptyWithRecursion(Object obj)`: 递归检查,包括集合内部的元素?但根据上面的分析,我们不需要递归检查集合内部的元素,因为只要集合有元素,整个对象就不为。所以,我们只需要递归检查JavaBean的属性。 因此,实际上,我们只需要一个方法,递归检查对象,但对于集合、数组、Map等,我们只检查它们本身是否为(即没有元素),而不检查内部元素的具体内容。 但是,对于JavaBean,我们递归检查其每个字段。 所以,我们写一个递归方法,使用一个Set<Object>来记录已经访问过的对象(防止循环引用)。 代码如下: ```java import java.lang.reflect.Field; import java.util.*; import java.util.stream.Stream; public class ObjectEmptyChecker { // 用于记录已经访问过的对象,防止循环引用 private static final ThreadLocal<Set<Object>> visitedObjects = ThreadLocal.withInitial(IdentityHashSet::new); /** * 判断对象是否为(包括其所有属性) * * @param obj 要检查的对象 * @return 如果对象及其所有属性都为,则返回true;否则返回false */ public static boolean isEmpty(Object obj) { // 重置ThreadLocal,每次调用都是独立的 Set<Object> currentVisited = visitedObjects.get(); try { return isEmptyInternal(obj, currentVisited); } finally { currentVisited.clear(); } } private static boolean isEmptyInternal(Object obj, Set<Object> visited) { // 如果对象为null,则为 if (obj == null) { return true; } // 防止循环引用:如果已经访问过该对象,则跳过,并认为(因为循环引用存在说明有对象) if (visited.contains(obj)) { return false; } visited.add(obj); // 检查数组 if (obj.getClass().isArray()) { int length = Array.getLength(obj); if (length == 0) { return true; } // 检查数组中的每个元素是否都为 for (int i = 0; i < length; i++) { Object element = Array.get(obj, i); if (!isEmptyInternal(element, visited)) { return false; } } return true; } // 检查集合 if (obj instanceof Collection) { Collection<?> collection = (Collection<?>) obj; if (collection.isEmpty()) { return true; } // 检查集合中的每个元素是否都为 for (Object element : collection) { if (!isEmptyInternal(element, visited)) { return false; } } return true; } // 检查Map if (obj instanceof Map) { Map<?, ?> map = (Map<?, ?>) obj; if (map.isEmpty()) { return true; } // 检查Map中的每个值是否都为(键不算,因为Map的通常指没有键值对) for (Object value : map.values()) { if (!isEmptyInternal(value, visited)) { return false; } } return true; } // 检查字符串 if (obj instanceof CharSequence) { return ((CharSequence) obj).length() == 0; } // 其他类型的对象:通过反射检查所有字段 Class<?> clazz = obj.getClass(); // 如果是基本类型或包装类型,则直接判断(因为已经不为null) if (clazz.isPrimitive() || isWrapperType(clazz)) { // 基本类型或包装类型,不为null,所以不为。但包装类型如果是0或false,是否算?这里我们按不为处理,因为对象存在。 // 如果需要检查包装类型的值,可以扩展,但这里不处理。 return false; } // 获取所有字段(包括父类) List<Field> fields = getAllFields(clazz); for (Field field : fields) { // 跳过静态字段 if (Modifier.isStatic(field.getModifiers())) { continue; } // 设置可访问 field.setAccessible(true); try { Object fieldValue = field.get(obj); // 递归检查字段值 if (!isEmptyInternal(fieldValue, visited)) { return false; } } catch (IllegalAccessException e) { // 忽略无法访问的字段 // 实际上,由于我们设置了field.setAccessible(true),所以一般不会出现 return false; // 保守起见,认为有字段不可访问,则不为?或者跳过?这里我们选择跳过,因为无法访问的字段我们无法判断,所以认为 } } return true; } // 获取类及其父类的所有字段 private static List<Field> getAllFields(Class<?> clazz) { List<Field> fields = new ArrayList<>(); while (clazz != null && clazz != Object.class) { fields.addAll(Arrays.asList(clazz.getDeclaredFields())); clazz = clazz.getSuperclass(); } return fields; } // 判断是否是包装类型 private static boolean isWrapperType(Class<?> clazz) { return clazz == Integer.class || clazz == Long.class || clazz == Double.class || clazz == Float.class || clazz == Boolean.class || clazz == Short.class || clazz == Byte.class || clazz == Character.class; } // 自定义IdentityHashSet,因为IdentityHashMap的key可以存null,但Set不能直接存null,但我们这里对象不为null才存 private static class IdentityHashSet<T> extends AbstractSet<T> { private static final Object PRESENT = new Object(); private final IdentityHashMap<T, Object> map; IdentityHashSet() { map = new IdentityHashMap<>(); } @Override public Iterator<T> iterator() { return map.keySet().iterator(); } @Override public int size() { return map.size(); } @Override public boolean contains(Object o) { return map.containsKey(o); } @Override public boolean add(T t) { return map.put(t, PRESENT) == null; } @Override public boolean remove(Object o) { return map.remove(o) == PRESENT; } @Override public void clear() { map.clear(); } } } ``` 注意: 1. 我们使用了ThreadLocal来保存已访问对象,这样可以在递归调用中共享,同时避免多线程问题。 2. 对于数组、集合、Map,我们递归检查了它们的元素。这是因为即使容器不为,但如果所有元素都是的,那么整个容器也可以认为是的?但是,这个定义可能不符合用户预期。例如,一个包含一个字符串的列表,按照我们的方法,字符串会被认为是的(因为字符串长度为0),所以这个列表会被认为是的?但是,列表本身有一个元素(字符串),所以用户可能认为这个列表不为。因此,我们需要重新考虑。 因此,我们调整逻辑:对于容器(数组、集合、Map),我们只检查容器本身是否为(没有元素),而不检查容器内的元素是否为。因为容器内的元素是否为,是元素自己的状态,不应该影响容器的状态。也就是说,容器只要有元素,就不为,不管元素内容如何。 所以,修改: - 数组:长度不为0 -> - 集合:不为集合(size>0)-> - Map:不为Map(size>0)-> 这样,我们就不需要递归检查容器内的元素了。 修改后的代码: ```java import java.lang.reflect.Array; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.*; public class ObjectEmptyChecker { private static final ThreadLocal<Set<Object>> visitedObjects = ThreadLocal.withInitial(IdentityHashSet::new); public static boolean isEmpty(Object obj) { Set<Object> currentVisited = visitedObjects.get(); try { return isEmptyInternal(obj, currentVisited); } finally { currentVisited.clear(); } } private static boolean isEmptyInternal(Object obj, Set<Object> visited) { if (obj == null) { return true; } if (visited.contains(obj)) { return false; } visited.add(obj); // 数组:检查长度 if (obj.getClass().isArray()) { return Array.getLength(obj) == 0; } // 集合:检查是否为集合 if (obj instanceof Collection) { return ((Collection<?>) obj).isEmpty(); } // Map:检查是否为Map if (obj instanceof Map) { return ((Map<?, ?>) obj).isEmpty(); } // 字符串:检查是否为字符串 if (obj instanceof CharSequence) { return ((CharSequence) obj).length() == 0; } // 基本类型和包装类型:不为(因为已经不为null,基本类型不会进入这里,因为基本类型不是对象) if (obj.getClass().isPrimitive() || isWrapperType(obj.getClass())) { return false; } // 其他对象:检查所有字段 Class<?> clazz = obj.getClass(); List<Field> fields = getAllFields(clazz); for (Field field : fields) { if (Modifier.isStatic(field.getModifiers())) { continue; } field.setAccessible(true); try { Object fieldValue = field.get(obj); if (!isEmptyInternal(fieldValue, visited)) { return false; } } catch (IllegalAccessException e) { // 保守处理:认为无法访问的字段 return false; } } return true; } // 获取类及其父类的所有字段 private static List<Field> getAllFields(Class<?> clazz) { List<Field> fields = new ArrayList<>(); while (clazz != null && clazz != Object.class) { fields.addAll(Arrays.asList(clazz.getDeclaredFields())); clazz = clazz.getSuperclass(); } return fields; } // 判断是否是包装类型 private static boolean isWrapperType(Class<?> clazz) { return clazz == Integer.class || clazz == Long.class || clazz == Double.class || clazz == Float.class || clazz == Boolean.class || clazz == Short.class || clazz == Byte.class || clazz == Character.class; } // 自定义IdentityHashSet private static class IdentityHashSet<T> extends AbstractSet<T> { private static final Object PRESENT = new Object(); private final IdentityHashMap<T, Object> map = new IdentityHashMap<>(); @Override public Iterator<T> iterator() { return map.keySet().iterator(); } @Override public int size() { return map.size(); } @Override public boolean contains(Object o) { return map.containsKey(o); } @Override public boolean add(T t) { return map.put(t, PRESENT) == null; } @Override public boolean remove(Object o) { return map.remove(o) == PRESENT; } @Override public void clear() { map.clear(); } } } ``` 这个工具类可以用于判断对象及其属性是否为。注意,对于容器类型,我们只判断容器本身是否为(没有元素),而不关心容器内的元素内容。对于JavaBean,我们递归检查其所有静态字段。 使用示例: ```java public class Test { private String name; private List<String> list; // 构造方法等省略 } Test test1 = new Test(); // 两个字段都是null System.out.println(ObjectEmptyChecker.isEmpty(test1)); // true Test test2 = new Test(); test2.setName(""); // 字符串 test2.setList(new ArrayList<>()); // 列表 System.out.println(ObjectEmptyChecker.isEmpty(test2)); // true Test test3 = new Test(); test3.setName(" "); // 格字符串,注意:我们的方法中,格字符串长度不为0,所以 System.out.println(ObjectEmptyChecker.isEmpty(test3)); // false Test test4 = new Test(); test4.setList(new ArrayList<>(Arrays.asList(""))); // 列表中有一个字符串,但列表不为(size=1) System.out.println(ObjectEmptyChecker.isEmpty(test4)); // false ``` 注意事项: 1. 由于反射访问私有字段,可能需要设置安全策略。 2. 性能:反射可能较慢,对于性能敏感场景慎用。 3
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值