如何解决空指针异常

news2024/11/27 14:38:24

NPE异常相信 Java 程序员都很熟悉,是 NullPointerException 的缩写;最近业务需求开发的有点着急,测试环境就时不时的来个NPE异常,特别的头疼;作为出镜率最高的异常之一,一旦入行Java开发,可以说它将伴随着你整个职业生涯;不管是新手小白、还是老司机,对NPE异常那是又“爱”又狠,爱的主要原因是处理起来简单,恨当然是一个不小心就会踩坑;为了提高代码的质量,NPE异常是必须要消灭掉的;
下面就通过10个妙招,来彻底解决NPE问题:
在这里插入图片描述

1.Objects 工具类

既然要解决空指针,自然就是提前对对象进行判空校验;通常情况下,会使用if( null != obj )进行对象校验;在 Java 7 中,专门提供工具类java.util.Objects,让对象的判空校验更加简单;

特点

  • Java 7 自带,不需要额外的依赖
  • 静态方法,使用简单
  • 仅支持对象判空

示例
Objects.isNull

判断对象是否为空,为null返回true,否则返回false

Object obj = null;
System.out.println(Objects.isNull(obj)); // true


obj = new Object();
System.out.println(Objects.isNull(obj)); // false

Objects.nonNull

和Objects.isNull相反;判断对象不为空,为null返回false,否则返回true

Object obj = null;
System.out.println(Objects.nonNull(obj)); // false

obj = new Object();
System.out.println(Objects.nonNull(obj)); // true
Objects.requireNonNull

校验非空,一旦对象为空,就会抛出空指针异常(NullPointerException),改方法可以自定义异常描述,方便异常之后能快速定位问题所在:

Object obj = null;
Objects.requireNonNull(obj);
// 自定义错误描述
Objects.requireNonNull(obj,"obj 对象为空");

执行输出:

Exception in thread "main" java.lang.NullPointerException: obj 对象为空
 at java.util.Objects.requireNonNull(Objects.java:228)
 at com.ehang.helloworld.controller.NullTest.t5(NullTest.java:97)
 at com.ehang.helloworld.controller.NullTest.main(NullTest.java:23)

在这里插入图片描述

2.字符串判空

字符串是开发过程中使用最多一种数据类型,因此对字符串的判断、校验也就必不可少了,原生的方式都是通过空对象,长度进行判断:

String str = "Java"
if ( null != str && s1.length() > 0 ){
 // 对str字符串进行使用
}

但是,对字符串的校验,除了判空之外,还有很多其他的场景,比如判断是不是空串(String str = ""),是不是只有空格(String str = " ")等等,那这些校验,就会麻烦一些了;不过木有关系,现成的工具类已经足够满足了。

Spring StringUtil工具类

org.springframework.util.StringUtilsString 框架自带的字符串工具类,功能比较单一,在教新的版本中,这个工具类的字符串判空方法已经被弃用了,所以不太建议使用了;

  • StringUtils.isEmpty

空对象以及空串的校验;

String s1 = null;
String s2 = "";
String s3 = " ";
System.out.println(StringUtils.isEmpty(s1)); // true
System.out.println(StringUtils.isEmpty(s2)); // true
System.out.println(StringUtils.isEmpty(s3)); // false

apache lang3 StringUtil工具类

apache lang3 StringUtil 工具类(org.apache.commons.lang3.StringUtils) 相比于Spring 框架带的工具类,要强大太对了,涵盖了对String 操作的所有封装;

判空校验的话主要有4个StringUtils.isEmpty、StringUtils.isNotEmpty、StringUtils.isBlank、StringUtils.isNotBlank

  • 依赖
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
</dependency>

StringUtils.isEmptyStringUtils.isNotEmpty

判断字符串对象是否为空,以及字符串长度是否为0;isEmptyisNotEmpty 校验结果相反;

String s1 = null;
String s2 = "";
String s3 = " ";
System.out.println(StringUtils.isEmpty(s1)); // true
System.out.println(StringUtils.isEmpty(s2)); // true
System.out.println(StringUtils.isEmpty(s3)); // false
System.out.println();
System.out.println(StringUtils.isNotEmpty(s1)); // false
System.out.println(StringUtils.isNotEmpty(s2)); // false
System.out.println(StringUtils.isNotEmpty(s3)); // true
StringUtils.isBlank、StringUtils.isNotBlank

StringUtils.isEmpty和StringUtils.isNotEmpty 判断的基础上,还会将字符串开头,结尾的空格去掉之后,判断长度是否大于0

String s1 = null;
String s2 = "";
String s3 = " ";
String s4 = " 1  2    ";
System.out.println(StringUtils.isBlank(s1)); // true  空对象
System.out.println(StringUtils.isBlank(s2)); // true  长度等于0
System.out.println(StringUtils.isBlank(s3)); // true  去掉前后空格之后,长度也等于0
System.out.println(StringUtils.isBlank(s4)); // false 去掉前后空格(1  2),长度大于0
System.out.println();
System.out.println(StringUtils.isNotBlank(s1)); // false
System.out.println(StringUtils.isNotBlank(s2)); // false
System.out.println(StringUtils.isNotBlank(s3)); // false
System.out.println(StringUtils.isNotBlank(s4)); // true
  • 其他功能

本文主要是探讨判空校验,lang3StringUtil 工具类几乎涵盖了所有关于String操作的封装,大大降低了我们处理 String 的复杂度,更多功能可参考官方文档

https://commons.apache.org/proper/commons-lang/apidocs/org/apache/commons/lang3/StringUtils.html

3.字符串比较

在对字符串进行比较的时候,也需要特别注意NPE异常;

如下示例:

public Boolean isEhang(String name) {
    if (name.equals("ehang")) {
        return true;
    }
    return false;
}

当如果namenull的时候,就会出现NPE异常;

可以做如下调整:

if ("ehang".equals(name))
...

这样就算namenull,即不会出现NPE异常,也能正常的判断;

4.Map、List、Set 判空

Map、List、Set 是经常会用到的数据结构,虽然他们都包含有isEmpty()方法,能判断容器中是否包含了元素,但是无法判断自生对象是否为空,一旦对象没有实例化时,调用isEmpty()就会报空指针异常;Spring 为我们提供了一个org.springframework.util.CollectionUtils工具类,其中的isEmpty就会优先判断对象是否为空,然后再通过isEmpty()判断是否存在元素,能大大减少因为对象为空带来的空指针异常;

Map map = null;
System.out.println(map.isEmpty()); // 空指针异常
System.out.println(CollectionUtils.isEmpty(map)); // true
map = new HashMap();
System.out.println(map.isEmpty()); // true
System.out.println(CollectionUtils.isEmpty(map)); // true
map.put("1", "2");
System.out.println(CollectionUtils.isEmpty(map)); // false
System.out.println(map.isEmpty()); // false

List list = null;
System.out.println(list.isEmpty()); // 空指针异常
System.out.println(CollectionUtils.isEmpty(list)); // true
list = new ArrayList();
System.out.println(list.isEmpty()); // true
System.out.println(CollectionUtils.isEmpty(list)); // true
list.add("1");
System.out.println(CollectionUtils.isEmpty(list)); // false
System.out.println(list.isEmpty()); // false

Set set = null;
System.out.println(set.isEmpty()); // 空指针异常
System.out.println(CollectionUtils.isEmpty(set)); // true
set = new TreeSet();
System.out.println(set.isEmpty()); // true
System.out.println(CollectionUtils.isEmpty(set)); // true
set.add("1");
System.out.println(CollectionUtils.isEmpty(set)); // false
System.out.println(set.isEmpty()); // false

除了判空之外,该工具类还包含了很多很实用的方法,比如获取第一个元素:firstElement() 、最后一个元素:lastElement()、是否包含某个元素:contains() 等等。
在这里插入图片描述

  • hutool的CollectionUtil
    单纯判空,前面SpringCollectionUtils已经足够,其他的功能也够满足绝大部分的使用场景;hutoolCollectionUtil提供了更加完善的功能,如果需要,也可以选用;

依赖:

<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.7.22</version>
</dependency>

方法列表:
在这里插入图片描述

5.赋初始值、尽量不要返回null对象

当定于局部变量,定义对象的属性时,能赋初始值的就尽量带上初始值;

Map map = new HashMap();
private Integer age = 0;

当方法有返回值的时候,非必要的情况下,尽量不要返回null

比如一个方法的执行最终返回的是List,当List没有值的时候,可以不返回null对象,而是可以返回一个空的List

public List select(){
    // 这里处理其他逻辑
    // 一旦返回的是null是,返回一个空List对象
    return Collections.emptyList();
}

6.Optional

OptionalJava 8 提供的一个对象容器,目的就是为了能有效的解决这个烦人的空指针异常,我们可以将 Optional 看成一个对象给包装类;

实例化 Optional 对象

Object o1 = null;
Optional<Object> op1 = Optional.of(o1);
Optional<Object> op2 = Optional.ofNullable(o1);

Optional.of()
当对象为null时,创建过程就会抛出NPE异常

Optional.ofNullable()
当对象为null时,也能正常返回 Optional 对象

  • 判空 isPresent()
Integer i1 = null;
Optional<Integer> op1 = Optional.of(i1);
System.out.println(op1.isPresent()); // false

Integer i2 = 123;
Optional<Integer> op2 = Optional.ofNullable(i2);
System.out.println(op2.isPresent()); // true
op2.ifPresent(i->{
    System.out.println(i);
});

isPresent()当对象为null返回true,不为空时返回false

lambda表示式的链式处理:

op2.ifPresent(obj->{
    System.out.println(obj);
});
  • 取值
// 取出原值,如果原对象为null会报NoSuchElementException异常
Integer integer = op2.get();
// 取出原值,如果原值为空,则返回指点的默认值
Integer integer1 = op1.orElse(456);
// 取出原值,如果原值为空,返回默认值,不过在返回之前还需要做一些其他的事情
Integer integer2 = op2.orElseGet(() -> {
    // 在这里做一些其他的操作
    return 456;
});

// 取出原值,如果原值为空,就抛出指定的异常
op2.orElseThrow(RuntimeException::new);
op2.orElseThrow(() -> new RuntimeException("不好,我的值是空的!"));
  • map() 和 flatMap()

编码过程中,经常会出现:a.xxx().yyy().zzz().mmm() 这样链式调用,这个过程,一旦中间有任意一环出现问题,就会NPE异常,因此,我们就可以借助map()flatMap()来避免这个问题;

测试对象:

@Data
@NoArgsConstructor
@AllArgsConstructor
static class User {
    private String name;

    private Integer age;

    private Optional<String> addr;
}

测试:

// 得到姓名的长度,如果没有姓名就返回0
Integer nameLen = Optional.of(new User(null, 10, null))
        .map(User::getName)
        .map(String::length)
        .orElse(0);
System.out.println(nameLen);

// 得到地址的长度,如果没有姓名就返回0
Integer addr = Optional.of(new User(null, 10, Optional.of("北京")))
        .flatMap(User::getAddr)
        .map(String::length)
        .orElse(0);
System.out.println(addr);

map会将返回的对象封装成Optional对象,如果返回的对象本身就是一个Optional对象了,那就使用flatMap()

 Optional<String> s = Optional.of(new User(null, 10, Optional.of("北京")))
                .flatMap(User::getAddr);
        Optional<Optional<String>> s1 = Optional.of(new User(null, 10, Optional.of("北京")))
                .map(User::getAddr);
        System.out.println("flatMap: "+s1);
        System.out.println("map: "+s);

在这里插入图片描述

7.断言

Spring 中的 org.springframework.util.Assert 翻译为中文为"断言",它用来断定某一个实际的运行值和预期项是否一致,不一致就抛出异常。借助这个类,同样也可以做判空检验;

Assert 类提供了以下的静态方法:

方法名描述失败时抛出异常
isNull(Object object, String message)object 不为空,抛出异常IllegalArgumentException
notNull(Object object, String message)object 为空,抛出异常 IllegalArgumentException
hasLength(String text, String message)text 是空字符串,抛出异常IllegalArgumentException
hasText(String text, String message)不包含空白字符串,抛出异常IllegalArgumentException
doesNotContain(String textToSearch, String substring, String message)textToSearch 中包含 substring,抛出异常IllegalArgumentException
notEmpty(Object[] array, String message)array 为空或长度为 1,抛出异常IllegalArgumentException
noNullElements(Object[] array, String message)array 中包含 null 元系,抛异常IllegalArgumentException
notEmpty(Collection collection, String message)collection 不包含元素,抛出异常IllegalArgumentException
notEmpty(Map map, String message)map 中包含 null,抛出异常IllegalArgumentException
isInstanceOf(Class type, Object obj, String message)如果 obj 不是 type 类型,抛出异常IllegalArgumentException
isAssignable(Class superType, Class subType, String message)subType 不是 superType 子类,抛出异常IllegalArgumentException
state(boolean expression, String message)expression 不为 true 抛出异常IllegalStateException
isTrue(boolean expression, String message)expression 不为 true 抛出异常IllegalArgumentException
Integer i1 = null;
Assert.notNull(i1,"i1 不为空");

Map map = null;
Assert.notEmpty(map,"map 不为空");

异常:

Exception in thread "main" java.lang.IllegalArgumentException: map 不为空
 at org.springframework.util.Assert.notEmpty(Assert.java:555)
 at com.ehang.helloworld.controller.NullTest.t6(NullTest.java:119)
 at com.ehang.helloworld.controller.NullTest.main(NullTest.java:23)

特别注意:

Assert 用来断定某一个实际的运行值和预期项是否一致,所以他和其他工具类的校验方式是反着在;比如isNull方法是期望对象为null,如果不为空的时候,就会报错;notNull表示期望对象不为空,当对象为空时,就会报错;

8.局部变量使用基本数据类型

文章《阿里为何禁止在对象中使用基本数据类型》中,从性能的角度,推荐局部变量的定义尽量使用基本数据类型,能不用包装类就不用;那么从今天文章的角度来说,使用基本数据类型也能有效的避免空指针异常;

如下实例:

int x;
Integer y;
System.out.println( x + 1 );  // 编译失败
System.out.println( y + 1 );  // 编译失败

int i = 1;
Integer j = null;
System.out.println( i + 1 );  // 正常
System.out.println( j + 1 );  // 空指针异常
int m = i; // 正常
int n = j; // 空指针异常

当变量x、y 只定义、不赋值的时候,x + 1y + 1 是没办法通过编译的;而包装类 j 是可以指定null对象,当包装类参与运算的时候,首先会做拆箱操作,也就是调用 intValue() 方法,由于对象是空的,调用方法自然就会报空指针;同时,将一个包装类赋值给一个基本数据类型时,同样也会做拆箱操作,自然也就空指针异常了;

但是,基本数据类型就必须指定一个具体值,后续不管运算、还是赋值操作,都不会出现空指针异常;

9.提前校验参数

后台数据,绝大部分都是通过终端请求传递上来的,所以需要在最接近用户的地方,把该校验的参数都校验了;比如StringBoot项目,就需要在Controller层将客户端请求的参数做校验,一旦必传的参数没有传值,就应该直接给客户端报错并提醒用户,而不是将这些不符合要求的null值传到Service甚至保存到数据库,尽早的校验并拦截,就能大大降低出问题的概率

hibernate-validator就能完美解决参数校验问题

10.IDEA提醒

IDEA 对空对象或者可能会出现null值的对象会有提醒,可以根据提醒来提前感知并预防

public static String t1(int i){
    String name1 = null;
    String name2 = null;
    if(i>0){
        name2 = "ehang";
    }
    t2(name1);
    t2(name2);
    return name2;
}
public static String t2(String s){
        System.out.println(s.length());
        return s;
    }

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/615608.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

微服务网关、SpringBoot、Nginx、tomcat8配置跨域

微服务网关、SpringBoot、Nginx、tomcat8配置跨域 跨域是什么?为什么会跨域解决跨域微服务网关处理跨域springboot项目配置跨域nginx配置跨域tomcat8配置跨域 跨域是什么? 跨域是A端向B端发送请求&#xff0c;A端与B端的地址协议、域名、端口三者之间任意一个不同&#xff0c…

Tomcat的优化

Tomcat的优化 一、Tomcat 优化Tomcat 配置文件参数优化 二、系统内核优化三、Tomcat 配置 JVM 参数&#xff1a;参数含义 一、Tomcat 优化 Tomcat默认安装下的缺省配置并不适合生产环境&#xff0c;它可能会频繁出现假死现象需要重启&#xff0c;只有通过不断压测优化才能让它…

吴恩达联手OpenAI的免费课程笔记—面向开发人员的 ChatGPT 提示工程

目录 前言一、大语言模型介绍二、提示指南2-0、导入API key和相关的python库2-1、写清楚的、具体的提示2-1-1、使用分隔符清楚的指示输入的不同部分2-1-2、要求结构化的输出2-1-3、按照指定的条件输出2-1-4、少样本学习 2-2、给模型时间去思考2-2-1、指定完成任务所需要的具体步…

软件外包开发UI管理工具

软件在开发前需要设计UI界面&#xff0c;UI界面是产品经理和开发人员、测试人员之间的交流工具&#xff0c;因此项目中会有多人的工作涉及到的UI界面&#xff0c;这就需要有个好的工具协调相互之间的工作。今天和大家分享一些常用到的工具&#xff0c;希望对大家的工作有所帮助…

插件框架PF4J-从理论到实践

PF4J:Plugin Framework for Java 目录 是什么&#xff1f; 不是什么&#xff1f; 特点 组件 主要类 流程概述 spring-pf4j 思考 功能模块化 我对pf4j的封装和使用demo GitHub - chlInGithub/pf4jDemo: pf4j demo 是什么&#xff1f; 开源轻量级的插件框架。通过插件…

三相三线、三相四线、三相五线制区别

三相三线、三相四线、三相五线制区别 1、三相三线2、三相四线3、三相五线4、三相三线和三相四线的区别5、三相四线和三相五线的区别 1、三相三线 由A、B、C这3根相线俗称火线组成&#xff0c;没有布置零线N和接地线PE&#xff0c;这种布线方式常见于交流380V的上一级10KV的系统…

Maven安装教程

maven环境配置&#xff08;点击此电脑右键属性&#xff09;&#xff1a; 点击高级系统设置&#xff0c;点击环境变量&#xff1a; 开始配置环境变量&#xff08;点击系统变量&#xff0c;新建按钮&#xff09;&#xff1a; 新建系统变量&#xff1a;MAVEN_HOMED:\maven\apac…

vueX学习看这篇就够了

vuex就是为了实现全局状态管理 vuex有哪些东西&#xff1f; state【状态】getter【可以认为是 store 的计算属性&#xff0c;不会修改状态】mutation【唯一修改state的方法&#xff0c;不支持异步】action【不能直接修改state,通过触发mutation修改状态&#xff0c;支持异步】…

GPT聊天功能,逐字返回数据

目录 前言一、前端二、后端1.接收前端请求的api如下是继续向其他接口请求的api如下是直接返回前端数据的api甚至可以返回图片 2.模拟GPT的接口 前言 我们在和GPT交流的时候发现GPT总是逐字的显示&#xff0c;因为GPT是一种基于神经网络的自然语言处理模型&#xff0c;它的训练…

王道考研计算机网络第一章知识点汇总

以上内容为1.1概念与功能的重点知识点 以下为1.2组成与分类&#xff1a; P2P模式下每台主机既可以是客户也可以是服务器&#xff0c;主机越多资源分享速度越快。 1.3标准化工作及相关组织 1.4性能指标 带宽只是指的是从主机内部往传输链路上投送数据的最大能力(从入口端放入数…

棱镜七彩中标浦发银行项目 助力金融行业开源治理

近日&#xff0c;棱镜七彩凭借出色的研发实力和优秀的产品服务能力在众多竞标企业中脱颖而出&#xff0c;成功中标上海浦东发展银行创新实验室“开源治理扫描工具信创改造课题”项目。棱镜七彩将为浦发银行在开源软件治理、软件安全可靠性等方面提供全方位支持。 在数字经济发…

odoo from 表单自定义按钮 执行JS代码 并调用websoket

业务场景&#xff1a; 集成串口读取RFID数据。由于串口是需要在客户端本地电脑执行才可以拿到数据 但是系统 部署在服务器 不能直接调用串口。 解决方案&#xff1a; 利用websoket通信 调用串口 传输 读取到的串口数据&#xff0c;解决服务器与本地之间的通信 本场景是基于odo…

深度解析:2023年软件测试的10个新趋势和挑战

随着技术的飞速发展&#xff0c;软件测试的角色和责任也在经历重大转变。我们在2023年目前所面临的一些新趋势和挑战值得所有从业人员关注。以下是这些主要趋势和挑战的深度分析。 趋势一&#xff1a;人工智能和机器学习在测试中的应用 AI和ML正在越来越多地应用于软件测试&am…

给httprunnermanager接口自动化测试平台演示参数化(五)

文章目录 一、背景1.1、前情回顾 二、参数化实现三、总结 一、背景 参数化&#xff0c;在使用httprunner框架的时候&#xff0c;参数话说实在的不是很实用&#xff0c;因为更多是场景化的用例编写&#xff0c;不用过多的去参数化批量执行&#xff0c;无非也就是登录注册查询啥的…

数据库系统概论 ---知识点大全(期末复习版)

&#xff08;一&#xff09;绪论 数据(Data)&#xff1a;是数据库中存储的基本对象 数据的定义&#xff1a;描述事物的符号记录 数据的种类&#xff1a;文字、图形、图象、声音等 数据的特点&#xff1a;数据与其语义是不可分的 数据库(Database,简称DB)&#xff1a;是长期…

数据结构与算法-跳表详解

我们知道如果一个数组是有序的&#xff0c;查询的时候可以使用二分法进行查询&#xff0c;时间复杂度可以降到 O(logn) &#xff0c;但如果链表是有序的&#xff0c;我们仍然是从前往后一个个查找&#xff0c;这样显然很慢&#xff0c;这个时候我们可以使用跳表&#xff08;Ski…

chatgpt赋能python:Python如何依次取字符——一种简单有效的方法

Python如何依次取字符——一种简单有效的方法 1. 介绍 Python 常常被用于编写文本处理脚本&#xff0c;而文本处理中的一个常见任务就是依次取字符。本文将介绍一种简单高效的方法&#xff0c;让您可以在 Python 中便捷地完成此操作。 2. 如何依次取字符 Python 中的字符串…

黑客入门必备指南

在探讨黑客如何入门之前&#xff0c;首先我们的思想要端正。 作为一名黑客&#xff0c;必须要有正直善良的价值观。 或许你听过这么一句话“能力越大&#xff0c;责任越大”作为一名黑客就是如此&#xff0c;黑客的技术越精湛&#xff0c;能力就越大&#xff0c;就越不能去干…

spark入门 Linux模式 Local模式 (二)

一、下载对应的spark包 https://archive.apache.org/dist/spark/spark-3.0.0/ 我这里下载的是spark-3.0.0-bin-hadoop3.2.tgz 二、解压 tar -zvxf spark-3.0.0-bin-hadoop3.2.tgz三、启动 再解压路径的bin目录执行下 ./spark-shell 四、测试 WordCount代码例子 sc.textFil…

接口测试-使用mock生产随机数据

在做接口测试的时候&#xff0c;有的接口需要进行大量的数据进行测试&#xff0c;还不能是重复的数据&#xff0c;这个时候就需要随机生产数据进行测试了。这里教导大家使用mock.js生成各种随机数据。 一、什么是mock.js mock.js是用于生成随*机数据&#xff0c;拦截 Ajax 请…