分类型讨论:自定义对象、字符串、集合、数组、级联调用判空、基本类型与包装类、Optional。
1.自定义对象判空
// 传统写法
User user = getUser();
if (user == null) {
System.out.println("用户不存在");
}
// 使用 Objects.isNull()
if (Objects.isNull(user)) {
// 处理 null
}
// 使用 Java 8 的 Optional(更优雅)
Optional.ofNullable(getUser())
.ifPresentOrElse(
u -> System.out.println("用户名: " + u.getName()),
() -> System.out.println("用户不存在")
);
// 或者
Optional<String> optional = Optional.ofNullable(str);
if (optional.isPresent()) {
// 对象不为空
} else {
// 对象为空
}
2.字符串判空
检查字符串是否为 null
或空字符串。使用isEmpty()
方法判断是否为空,结合isBlank()
方法判断是否为空白字符串。
String input = getInput();
// 传统写法
if (input == null || input.isEmpty()) {
System.out.println("输入不能为空");
}
// 使用 Apache Commons Lang(需引入依赖)(推荐)
if (StringUtils.isEmpty(input)) {
System.out.println("输入不能为空");
}
// Java 11+ 写法(检查空或纯空格)
if (input == null || input.isBlank()) {
System.out.println("输入无效");
}
// 总结:使用Apache Commons Lang库的StringUtils
// StringUtils.isEmpty(input):判断字符串是否为null或长度为0。
// StringUtils.isNotBlank(input):判断字符串是否非null、非空且不全是空白字符。
if (StringUtils.isEmpty(input)) {
// 字符串为null或空
}
if (StringUtils.isNotBlank(input)) {
// 字符串非null、非空且不全是空白字符
}
3. 集合判空
检查 List
是否为空。
List<String> list = getList();
// 传统写法
if (list == null || list.isEmpty()) {
System.out.println("列表为空");
}
// 使用 Apache Commons Collections(推荐)
if (CollectionUtils.isEmpty(list)) {
System.out.println("列表为空");
}
// 防御性编程:返回空集合而不是 null
public List<String> getList() {
return new ArrayList<>(); // 永远不返回 null
}
检查Map是否为空。
Map<String, String> map = new HashMap<>();
if (map == null || map.isEmpty()) {
// Map为null或者为空
}
if (map != null && map.containsKey("keyName")) {
// 存在指定键
}
// Apache Commons Collections库提供了MapUtils类,可以简化一些常见的操作,比如判空。(推荐)
if (MapUtils.isEmpty(map)) {
// Map为null或者为空
}
4. 数组判空
检查数组是否为 null
或空数组。
String[] array = getArray();
// 传统写法
if (array == null || array.length == 0) {
System.out.println("数组为空");
}
// 使用 Apache Commons Lang (推荐)
if (ArrayUtils.isEmpty(array)) {
System.out.println("数组为空");
}
5. 级联调用判空(链式调用)
场景:安全获取 用户 -> 地址 -> 城市
。
User user = getUser();
// 传统写法(容易漏判)
if (user != null
&& user.getAddress() != null
&& user.getAddress().getCity() != null) {
System.out.println("城市: " + user.getAddress().getCity());
}
// 使用 Optional(更简洁)(推荐)
Optional.ofNullable(user)
.map(User::getAddress)
.map(Address::getCity)
.ifPresent(city -> System.out.println("城市: " + city));
6. 基本类型与包装类
基本类型(如int
, long
, double
, char
, boolean
等)与它们的包装类型(如Integer
, Long
, Double
, Character
, Boolean
等)有所不同。基本类型不能为null
,因为它们不是对象;而包装类型可以为null
。因此,在处理基本类型的“空值”时,通常是指处理包装类型的null
值或者使用特定的默认值来表示“空”。
Java中基本类型的默认值:
-
byte:默认值为
0
-
short:默认值为
0
-
int:默认值为
0
-
long:默认值为
0L
-
float:默认值为
0.0f
-
double:默认值为
0.0d
-
char:默认值为
\u0000
(即NUL字符) -
boolean:默认值为
false
包装类型
Integer num = null;
if (num == null) {
// 数值为空
}
// 使用Optional:对于可能为null的包装类型,可以使用Optional来简化代码。
Optional<Integer> optionalNum = Optional.ofNullable(num);
if (optionalNum.isPresent()) {
// 不为空,可以安全地使用optionalNum.get()
} else {
// 为空
}
// 使用默认值
// 当你从数据库或其他外部源读取数据,并且期望基本类型的值时,如果遇到null,可以选择提供一个默认值。
Integer num = getNumberFromDatabase(); // 可能返回null
int actualNum = (num != null) ? num : -1; // 使用-1作为默认值
// 或者,更优雅的方式是使用Optional的orElse()方法:
int actualNum = Optional.ofNullable(getNumberFromDatabase()).orElse(-1);
场景:将 Integer
安全转为 int
。
Integer num = getNumber();
// 传统写法(可能 NPE)
int value = num; // 如果 num 是 null,这里会报错!
// 安全写法
int value = (num != null) ? num : 0;
// 使用 Java 8 Optional
int value = Optional.ofNullable(num).orElse(0);
7.Optional类型
判空方法:使用isPresent()
方法判断是否有值,使用orElse()
或orElseGet()
方法获取值或提供默认值
Optional<String> optional = Optional.ofNullable(str);
String value = optional.orElse("default value");
当使用 Optional
判断对象时,如果对象不为空需要处理复杂的逻辑,可以通过 链式调用 或 分步处理 来保持代码清晰。以下是几种常见场景的写法示例:
场景 1:简单处理(单行逻辑)
如果处理逻辑较简单,可直接在 ifPresent
中完成:
Optional.ofNullable(user)
.ifPresent(u -> {
System.out.println("用户名: " + u.getName());
u.setLastLoginTime(LocalDateTime.now());
// 其他简单操作...
});
场景 2:复杂逻辑(多步骤处理)
如果逻辑较复杂,可以将处理逻辑封装到 独立方法 中,保持代码可读性:
Optional.ofNullable(user)
.ifPresent(this::processUser);
// 定义独立方法处理逻辑
private void processUser(User user) {
// 步骤 1:验证用户状态
if (user.isDisabled()) {
log.error("用户已被禁用: {}", user.getId());
return;
}
// 步骤 2:更新用户信息
user.setLastLoginTime(LocalDateTime.now());
// 步骤 3:保存到数据库
userRepository.save(user);
// 更多操作...
}
场景 3:链式处理(转换或过滤数据)
如果需要基于对象的值进行链式操作(如转换、过滤),可用 map
和 filter
:
Optional.ofNullable(user)
.filter(u -> u.getAge() >= 18) // 过滤未成年用户
.map(u -> convertToDto(u)) // 转换为 DTO 对象
.ifPresent(dto -> {
sendNotification(dto);
auditService.logAction(dto.getId());
});
场景 4:处理异常或回退逻辑
如果处理过程中可能抛出异常,或需要回退操作,可结合 orElseThrow
和 orElse
:
// 示例 1:对象不存在时抛出异常
User user = Optional.ofNullable(getUser())
.orElseThrow(() -> new RuntimeException("用户不存在"));
// 示例 2:对象存在时处理,否则执行回退逻辑
Optional.ofNullable(getOrder())
.ifPresentOrElse(
order -> {
processPayment(order);
updateInventory(order);
},
() -> {
log.warn("订单不存在");
createFallbackOrder();
}
);
场景 5:返回处理结果
如果处理逻辑需要返回结果,可以用 map
返回新值:
// 示例:处理用户并返回摘要信息
String summary = Optional.ofNullable(user)
.map(u -> {
String name = u.getName();
int age = u.getAge();
return String.format("姓名: %s, 年龄: %d", name, age);
})
.orElse("用户不存在");
System.out.println(summary);
最佳实践总结
-
拆分逻辑:将复杂操作拆分为独立方法,避免 Lambda 表达式过于臃肿。
-
链式调用:利用
map
、filter
、flatMap
实现声明式编程。 -
异常处理:使用
orElseThrow
明确抛出异常。 -
副作用操作:在
ifPresent
中处理无返回值的操作(如保存数据、发送消息)。 -
避免滥用:不要用
Optional
替代所有null
检查,仅在需要明确表达“可能为空”时使用。
总结
-
核心原则:在可能为
null
的地方显式检查,或使用工具类简化代码。 -
工具推荐:
-
原生 Java:
Optional
、Objects
(Java 8+)。 -
第三方库:Apache Commons Lang、Guava。
-
-
关键习惯:返回空集合、方法入口校验参数、避免深层嵌套判空。