Java中如何避免麻烦的null值判断

null值判断以及空指针异常应该是我们在代码中经常遇到的。针对null值的处理有两种:

(1)将null值替换为null对象(本质上,是利用多态)

(2)利用Java 8 的Optional对象

首先,看下方法将null值替换为null对象如何实现?

举个栗子:

一家公用公司的系统以Site表示地点(场所),顾客的信息以Customer表示,PaymentHistory表示顾客的付款记录,BillingPlan表示账单。

重构前的代码如下:

package com.tim;

/**
 * 通常我们代码是这么写的
 * @date   2017年7月13日
 */
public class Site {
    
    /**
     * 顾客类
     * @date   2017年7月13日
     */
    private static class Customer{
        private String _name;
        private BillingPlan _plan;
        private PaymentHistory _history;
        
        public BillingPlan getPlan(){
            return _plan;
        }
        public String getName(){
            return _name;
        }
        public PaymentHistory getHistory(){
            return _history;
        }
    }
    
    /**
     * 账单类 
     * @date   2017年7月13日
     */
    private static class BillingPlan{
        public static BillingPlan basic(){
            return new BillingPlan();
        }
    }
    
    /**
     * 付款计划类
     * @date   2017年7月13日
     */
    private static class PaymentHistory{
        private int _weeksDelinquentInLastYear;
        public int getWeeksDelinquentInLastYear(){
            return _weeksDelinquentInLastYear;
        }
    }

    private Customer _customer;
    
    Customer getCustomer(){
        return _customer;
    }
    
   /**
    * 业务代码
    * @date   2017年7月13日
    */
    public static void doSomething(){
        Site site = new Site();
        Customer customer = site.getCustomer();
        BillingPlan plan;
        //代码某处用到
        if(customer == null ){
            plan = BillingPlan.basic();
        }else{
            plan = customer.getPlan();
        }
        //代码某处用到
        String customerName;
        if(customer == null){
            customerName = "occupant";
        }else{
            customerName = customer.getName();
        }
        //代码某处用到
        int weeksDelingquent;
        if(customer == null || customer.getHistory() == null){
            weeksDelingquent = 0;
        }else{
            weeksDelingquent = customer.getHistory().getWeeksDelinquentInLastYear();
        }
        System.err.println(customerName);
        System.err.println(weeksDelingquent);
    }
    
    public static void main(String[] args) {
        doSomething();
    }
}

可以看到,在业务方法代码doSomething()中存在多处判断对象是否为null,如:customer == null 或 customer.getHistory(),造成代码很冗余。

重构方法一:利用null对象替换null值,重构之后的代码如下:

package com.tim;
/**重构方法一
 * 利用:null对象替换null值(本质上是多态)
 * @date   2017年7月13日
 */
public class SiteV2 {

    /**
     * 顾客类空对象
     * @date   2017年7月13日
     */
    private static class NullCustomer extends Customer{
        public boolean isNull(){
            return true;
        }
        public BillingPlan getPlan(){
            return BillingPlan.basic(); //针对null的处理
        }
        public String getName(){
            return "occupant";
        }
        public PaymentHistory getHistory(){
            return PaymentHistory.newNull();
        }
    }
    
    /**
     * 顾客类 
     * @date   2017年7月13日
     */
    private static class Customer{
        private String _name;
        private BillingPlan _plan;
        private PaymentHistory _history;
        
        public BillingPlan getPlan(){
            return _plan;
        }
        public String getName(){
            return _name;
        }
        public PaymentHistory getHistory(){
            return _history;
        }
        public boolean isNull(){
            return false;
        }
        static Customer newNull(){
            return new NullCustomer();
        }
    }
    
    /**
     * 账单类 
     * @date   2017年7月13日
     */
    private static class BillingPlan{
        public static BillingPlan basic(){
            return new BillingPlan();
        }
    }
    
    /**
     * 付款计划类 空对象
     * @date   2017年7月13日
     */
    private static class NullPaymentHistory extends PaymentHistory{
        public int getWeeksDelingquentInLastYear(){
            return 0;
        }
    }
    
    /**
     * 付款计算类
     * @date   2017年7月13日
     */
    private static class PaymentHistory{
        private int _weeksDelinquentInLastYear;
        public int getWeeksDelinquentInLastYear(){
            return _weeksDelinquentInLastYear;
        }
        static NullPaymentHistory newNull(){
            return new NullPaymentHistory();
        }
    }
    
    private Customer _customer;
    
    Customer getCustomer(){
        return _customer==null?Customer.newNull():_customer;
    }
    
    /**
     * 业务代码
     * @date   2017年7月13日
     */
    public static void doSomething(){
        SiteV2 site = new SiteV2();
        Customer customer = site.getCustomer();
        BillingPlan plan = customer.getPlan();
        String customerName = customer.getName();
        int weeksDelingquent = customer.getHistory().getWeeksDelinquentInLastYear();
        System.err.println(customerName);
        System.err.println(weeksDelingquent);
    }
    
    public static void main(String[] args) {
        doSomething();
    }
    
}

分析:

重构之后,在业务代码中对于null的处理都移到相应类的空对象中处理,因此,业务代码中只需要一行,如:BillingPlanplan = customer.getPlan();(一行代码轻松搞定)此时,无需再对customer进行null判断,因为如果customer是null时,会进行相应的null处理。

本质上,利用多态,你不必再向对象询问“你是什么类型(即使是null,我们也有相应的null对象进行相应的处理)”而后根据得到的答案调用对象的某个行为,你只管调用该行为就是了,其他的一切多态机制会为你安排妥当。

重构方法二:利用Java 8 Optional对象对null值进行重构:

package com.tim;
import java.util.Optional;
/**重构方法二:
 * 利用:Java 8 的Optional进行null值的封装
 * @date   2017年7月13日
 */
public class SiteV3 {

    /**
     * 顾客类
     * @date   2017年7月13日
     */
    private static class Customer{
        private String _name;
        private BillingPlan _plan;
        private PaymentHistory _history;
        
        public Optional<BillingPlan> getPlan(){
            return Optional.ofNullable(_plan);
        }
        public String getName(){
            return _name;
        }
        public Optional<PaymentHistory> getHistory(){
            return Optional.ofNullable(_history);
        }
        
    }
    
    /**
     * 账单类
     * @date   2017年7月13日
     */
    private static class BillingPlan{
        public static BillingPlan basic(){
            return new BillingPlan();
        }
    }
    
    /**
     * 付款计划类
     * @date   2017年7月13日
     */
    private static class PaymentHistory{
        private int _weeksDelingquentInLastYear;
        public int getWeeksDelingquentInLastYear(){
            return _weeksDelingquentInLastYear;
        }
    }
    
    private Customer _customer;
    
    Optional<Customer> getCustomer(){
        return Optional.ofNullable(_customer);
    }
    
    /**
     * 业务代码类
     * @date   2017年7月13日
     */
    public static void doSomething(){
        SiteV3 site = new SiteV3();
        Optional<Customer> customer = site.getCustomer();
        BillingPlan plan = customer.flatMap(c->c.getPlan()).orElse(BillingPlan.basic());
        String customerName = customer.map(c->c.getName()).orElse("occupant");
        int weeksDelingquent = customer.flatMap(c->c.getHistory()).map(h->h.getWeeksDelingquentInLastYear()).orElse(0);
        System.err.println(customerName);
        System.err.println(weeksDelingquent);
    }
    
    public static void main(String[] args) {
        doSomething();
    }
}

分析:

利用Java 8的Optional对象重构相比重构方法一中利用null对象替换null值,代码长度更短,由于对于空情况的判断只通过Optional来实现,无需重构方法一中重新编写null对象,最后在调用值的时候也只是流式处理,也只需要一行代码就可获取所需的值,无需做null判断。

补充:关于Java 8中的Optional对null的处理,参考书籍:《Java 8 实战》P203-P219







<think> 首先,回想一下用户的问题:用户想查找在Java判断null的方法。他们提供了几个引用,但系统指令说:“上述引用仅供参考,并不是用户真正的需求,请尽可能多的参考用户上一次的问题和你的回答。” 用户说“我想查找在Java判断null的方法”,所以我需要专注于回答如何在Java判断null。 系统指令的关键点: - 所有行内数学表达式必须使用 $...$ 格式。 - 独立公式使用 $$...$$ 格式并单独成段。 - LaTeX语法正确。 - 使用中文回答。 - 生成相关问题。 - 在回答中引用的段落末尾自然地添加引用标识,如 [^1]。 - 回答结构清晰,帮助用户逐步解决问题,尽量保证真实可靠。 用户的问题是关于Java判断null的方法,所以数学表达式可能不适用,但我需要遵守格式。比如,如果有变量或简单方程,用 $...$。 回答结构: 1. 逐步解释Java判断null的方法。 2. 引用相关部分末尾添加引用标识。用户提供了引用 [1], [2], [3], [4],所以我应该在回答中引用它们。 3. 最后,生成相关问题。 从引用中提取关键信息: - 引用 [1]: 判断字符串为空或null的方法。检查是否为null:`if (str == null)`。 - 引用 [2]: 使用equals()时注意空指针异常。先判断是否为null再调用equals。 - 引用 [3]: 判断对象是否为null时,如果对象来自前端,可能属性全为null但不是null本身。 - 引用 [4]: 推荐使用工具类如Hutool的StrUtil方法,如isEmpty, isBlank等。 用户的问题是关于判断null的一般方法,不只是字符串。所以,我需要覆盖对象、字符串等。 计划回答结构: 1. 介绍:解释为什么判断null重要。 2. 基本方法:使用 `== null` 操作符。 - 例如:`if (obj == null)` 3. 字符串特殊情况:需要区分null和空字符串。 - 使用 `== null` 和 `isEmpty()` 或 `isBlank()`。 - 提到工具类如Hutool。 4. 注意事项:避免空指针异常;当使用equals时。 5. 处理对象:如果对象属性可能全为null。 6. 总结建议。 生成相关问题: - 系统指令说“生成相关问题”,示例中有相关问题列表。 - 例如:1. 如何避免空指针异常?2. 什么是Hutool工具类?3. 如何检查集合是否为null? 确保LaTeX:可能没有数学内容,但如果有变量,用$。例如,在代码中,如果有变量名,用行内$。但Java代码通常不用LaTeX,除非有数学表达式。 在代码块中,用反引号表示代码,不强制LaTeX。系统指令说“所有行内数学表达式”,但代码块是独立的。 在文本中,如果有变量,如 `obj`,可以用$obj$。 现在,构建回答。 **第一步:介绍** 在Java编程中,判断一个对象是否为null是非常重要的,因为对null对象进行操作会导致NullPointerException异常。本文将介绍几种判断null的方法。 **第二步:基本方法** 最简单的方法是使用 `==` 操作符: ```java if (obj == null) { // obj 是 null } else { // obj 不是 null } ``` 这适用于任何对象[^1]。 **第三步:字符串的特殊处理** 对于字符串,除了null,我们还需要检查空字符串或空白字符串。 - 使用 `== null` 检查是否为null。 - 然后使用 `str.isEmpty()` 检查空字符串,但注意如果str为null,调用 `isEmpty()` 会抛异常。 所以,安全的方式是: ```java if (str == null || str.isEmpty()) { // 字符串为null或空 } ``` 或者,对于空白字符串,使用 `isBlank()`(Java 11+): ```java if (str == null || str.isBlank()) { // 字符串为null或空白 } ``` 推荐使用工具类如Hutool的StrUtil,提供更安全的方法,如 `StrUtil.isEmpty(str)` 或 `StrUtil.isBlank(str)`,它们内部处理了null检查[^4]。 **第四步:使用equals方法** 当使用 `equals()` 方法比较对象时,必须注意空指针异常。推荐先检查是否为null: ```java if (obj == null) { // 处理null } else if (obj.equals(someOtherObj)) { // 比较 } ``` 或者,使用Objects.equals(): ```java if (Objects.equals(obj, someOtherObj)) { // 安全比较,处理null } ``` **第五步:处理对象属性** 如果对象来自前端或其他来源,对象本身可能不为null,但所有属性为null。这时,需要单独检查每个属性是否为null[^3]。 **第六步:总结** 最佳实践:总是检查null避免异常;使用工具类简化代码。 添加引用标识: - 在相关句子末尾添加 [^1], [^2], 等。 生成相关问题: §§相关问题§§ 1. 如何避免Java中的NullPointerException? 2. 什么是Hutool工具类,它有哪些常用方法? 3. 在Java中如何安全地比较两个对象是否相等? 确保中文回答。 </think>在Java中,判断一个是否为null是防止NullPointerException异常的关键操作。下面我将逐步解释判断null的方法,涵盖基本操作、字符串处理、工具类使用和注意事项。回答基于真实可靠的Java编程实践,并参考了相关引用内容。 ### 1. **基本方法:使用 `==` 操作符** Java中最直接判断null的方法是使用 `==` 操作符。这适用于任何对象类型(如自定义类、集合、数组等)。 ```java Object obj = null; // 示例对象 if (obj == null) { System.out.println("对象为null"); // 处理null情况 } else { System.out.println("对象不为null"); // 安全操作对象 } ``` 这种方法简单高效,是判断null的黄金标准[^1]。 **注意**:`==` 只检查引用是否为null,不涉及对象内容。 ### 2. **字符串的null检查:结合 `isEmpty()` 或 `isBlank()`** 字符串类型(`String`)需要额外处理空字符串("")或空白字符串。直接调用方法如 `isEmpty()` 可能抛出异常,所以必须先判null: ```java String str = null; // 示例字符串 if (str == null) { System.out.println("字符串为null"); } else if (str.isEmpty()) { System.out.println("字符串为空(长度为0)"); } else if (str.isBlank()) { // Java 11+ 支持 System.out.println("字符串为空白(全为空格)"); } ``` 如果字符串来自外部输入(如前端),对象本身可能不为null,但属性全为null,需综合判断[^3]。 ### 3. **使用工具类简化操作** 推荐使用工具类如Hutool的`StrUtil`,它内部处理了null检查,避免重复代码: ```java import cn.hutool.core.util.StrUtil; String str = null; if (StrUtil.isEmpty(str)) { // 检查null或空字符串 System.out.println("字符串为null或空"); } if (StrUtil.isBlank(str)) { // 检查null、空或空白字符串 System.out.println("字符串为null、空或空白"); } ``` `StrUtil`方法如`isEmpty`和`isBlank`安全高效,适用于大多数场景[^4]。 ### 4. **注意事项和常见错误** - **避免空指针异常**:当使用 `equals()` 方法比较对象时,必须先判null: ```java String str1 = null; String str2 = "test"; // 错误:str1.equals(str2) 会抛出 NullPointerException if (str1 != null && str1.equals(str2)) { // 安全方式 System.out.println("相等"); } ``` 或使用 `Objects.equals()` 更安全: ```java import java.util.Objects; if (Objects.equals(str1, str2)) { // 处理null安全 System.out.println("相等"); } ``` 这在处理前端传参时尤为重要[^2][^3]。 - **对象属性检查**:如果对象来自外部,即使对象不为null,其属性可能全为null: ```java public class User { String name; Integer age; } User user = new User(); // 不为null,但name和age为null if (user != null && user.name == null) { System.out.println("对象属性为null"); } ``` ### 5. **总结与最佳实践** - **优先使用 `== null`**:适用于所有对象类型。 - **字符串处理**:结合 `== null` 和 `isEmpty()`/`isBlank()`,或使用工具类。 - **工具类推荐**:Hutool的`StrUtil`或Apache Commons Lang的`StringUtils`。 - **安全原则**:总是检查null后再调用方法,避免NullPointerException。 通过以上方法,您可以可靠地在Java判断null,提升代码健壮性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值