Java判空不再难,Java程序员必看

分类型讨论:自定义对象、字符串、集合、数组、级联调用判空、基本类型与包装类、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:链式处理(转换或过滤数据)

如果需要基于对象的值进行链式操作(如转换、过滤),可用 mapfilter

Optional.ofNullable(user)
    .filter(u -> u.getAge() >= 18)  // 过滤未成年用户
    .map(u -> convertToDto(u))      // 转换为 DTO 对象
    .ifPresent(dto -> {
        sendNotification(dto);
        auditService.logAction(dto.getId());
    });

场景 4:处理异常或回退逻辑

如果处理过程中可能抛出异常,或需要回退操作,可结合 orElseThroworElse

// 示例 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);

最佳实践总结

  1. 拆分逻辑:将复杂操作拆分为独立方法,避免 Lambda 表达式过于臃肿。

  2. 链式调用:利用 mapfilterflatMap 实现声明式编程。

  3. 异常处理:使用 orElseThrow 明确抛出异常。

  4. 副作用操作:在 ifPresent 中处理无返回值的操作(如保存数据、发送消息)。

  5. 避免滥用:不要用 Optional 替代所有 null 检查,仅在需要明确表达“可能为空”时使用。

总结

  • 核心原则:在可能为 null 的地方显式检查,或使用工具类简化代码。

  • 工具推荐

    • 原生 Java:OptionalObjects(Java 8+)。

    • 第三方库:Apache Commons Lang、Guava。

  • 关键习惯:返回空集合、方法入口校验参数、避免深层嵌套判空。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值