你这个问题的解决

问题定位:

在堆栈异常信息的第一行就可以定位到是哪里出了空指针,倘若这里不是你写的类,可以往下翻一下,找到你写的类,就是这里出现的空指针。

问题解决:

对一个空对象调用里面的方法或者属性的时候会报空指针,检查这个对象为什么是空即可。

Java 空指针异常的若干解决方案

Java 中任何对象都有可能为空,当我们调用空对象的方法时就会抛出 NullPointerException 空指针异常,这是一种非常常见的错误类型。我们可以使用若干种方法来避免产生这类异常,使得我们的代码更为健壮。本文将列举这些解决方案,包括传统的空值检测、编程规范、以及使用现代 Java 语言引入的各类工具来作为辅助。

运行时检测

最显而易见的方法就是使用 if (obj == null) 来对所有需要用到的对象来进行检测,包括函数参数、返回值、以及类实例的成员变量。当你检测到 null 值时,可以选择抛出更具针对性的异常类型,如 IllegalArgumentException,并添加消息内容。我们可以使用一些库函数来简化代码,如 Java 7 开始提供的 Objects#requireNonNull 方法:

public void testObjects(Object arg) {

Object checked = Objects.requireNonNull(arg, "arg must not be null");

checked.toString();

}

Guava 的 Preconditions 类中也提供了一系列用于检测参数合法性的工具函数,其中就包含空值检测:

public void testGuava(Object arg) {

Object checked = Preconditions.checkNotNull(arg, "%s must not be null", "arg");

checked.toString();

}

我们还可以使用 Lombok 来生成空值检测代码,并抛出带有提示信息的空指针异常:

public void testLombok(@NonNull Object arg) {

arg.toString();

生成的代码如下:

public void testLombokGenerated(Object arg) {

if (arg == null) {

throw new NullPointerException("arg is marked @NonNull but is null");

}

arg.toString();

}

这个注解还可以用在类实例的成员变量上,所有的赋值操作会自动进行空值检测。

编程规范

·通过遵守某些编程规范,也可以从一定程度上减少空指针异常的发生。

使用那些已经对 null 值做过判断的方法,如 String#equals、String#valueOf、以及三方库中用来判断字符串和集合是否为空的函数:

if (str != null && str.equals("text")) {}

if ("text".equals(str)) {}

if (obj != null) { obj.toString(); }

String.valueOf(obj); // "null"

// from spring-core

StringUtils.isEmpty(str);

CollectionUtils.isEmpty(col);

// from guava

Strings.isNullOrEmpty(str);

// from commons-collections4

CollectionUtils.isEmpty(col);

·如果函数的某个参数可以接收 null 值,考虑改写成两个函数,使用不同的函数签名,这样就可以强制要求每个参数都不为空了:

public void methodA(Object arg1) {

methodB(arg1, new Object[0]);

}

public void methodB(Object arg1, Object[] arg2) {

for (Object obj : arg2) {} // no null check

}

·如果函数的返回值是集合类型,当结果为空时,不要返回 null 值,而是返回一个空的集合;如果返回值类型是对象,则可以选择抛出异常。Spring JdbcTemplate 正是使用了这种处理方式:

// 当查询结果为空时,返回 new ArrayList<>()

jdbcTemplate.queryForList("SELECT * FROM person");

// 若找不到该条记录,则抛出 EmptyResultDataAccessException

jdbcTemplate.queryForObject("SELECT age FROM person WHERE id = 1", Integer.class);

// 支持泛型集合

public List testReturnCollection() {

return Collections.emptyList();

}

静态代码分析

Java 语言有许多静态代码分析工具,如 Eclipse IDE、SpotBugs、Checker Framework 等,它们可以帮助程序员检测出编译期的错误。结合 @Nullable 和 @Nonnull 等注解,我们就可以在程序运行之前发现可能抛出空指针异常的代码。

但是,空值检测注解还没有得到标准化。虽然 2006 年 9 月社区提出了 JSR 305 规范,但它长期处于搁置状态。很多第三方库提供了类似的注解,且得到了不同工具的支持,其中使用较多的有:javax.annotation.Nonnull:由 JSR 305 提出,其参考实现为 com.google.code.findbugs.jsr305;

org.eclipse.jdt.annotation.NonNull:Eclipse IDE 原生支持的空值检测注解;

edu.umd.cs.findbugs.annotations.NonNull:SpotBugs 使用的注解,基于 findbugs.jsr305;

org.springframework.lang.NonNull:Spring Framework 5.0 开始提供;

org.checkerframework.checker.nullness.qual.NonNull:Checker Framework 使用;

android.support.annotation.NonNull:集成在安卓开发工具中;

我建议使用一种跨 IDE 的解决方案,如 SpotBugs 或 Checker Framework,它们都能和 Maven 结合得很好。

SpotBugs 与 @NonNull、@CheckForNull

SpotBugs 是 FindBugs 的后继者。通过在方法的参数和返回值上添加 @NonNull 和 @CheckForNull 注解,SpotBugs 可以帮助我们进行编译期的空值检测。需要注意的是,SpotBugs 不支持 @Nullable 注解,必须用 @CheckForNull 代替。如官方文档中所说,仅当需要覆盖 @ParametersAreNonnullByDefault 时才会用到 @Nullable。

官方文档 中说明了如何将 SpotBugs 应用到 Maven 和 Eclipse 中去。我们还需要将 spotbugs-annotations 加入到项目依赖中,以便使用对应的注解。

com.github.spotbugs

spotbugs-annotations

3.1.7

以下是对不同使用场景的说明:

@NonNull

private Object returnNonNull() {

// 错误:returnNonNull() 可能返回空值,但其已声明为 @Nonnull

return null;

}

@CheckForNull

private Object returnNullable() {

return null;

}

public void testReturnNullable() {

Object obj = returnNullable();

// 错误:方法的返回值可能为空

System.out.println(obj.toString());

}

private void argumentNonNull(@NonNull Object arg) {

System.out.println(arg.toString());

}

public void testArgumentNonNull() {

// 错误:不能将 null 传递给非空参数

argumentNonNull(null);

}

public void testNullableArgument(@CheckForNull Object arg) {

// 错误:参数可能为空

System.out.println(arg.toString());

}

对于 Eclipse 用户,还可以使用 IDE 内置的空值检测工具,只需将默认的注解 org.eclipse.jdt.annotation.Nullable 替换为 SpotBugs 的注解即可:

Checker Framework 与 @NonNull、@Nullable

Checker Framework 能够作为 javac 编译器的插件运行,对代码中的数据类型进行检测,预防各类问题。我们可以参照 官方文档,将 Checker Framework 与 maven-compiler-plugin 结合,之后每次执行 mvn compile 时就会进行检查。Checker Framework 的空值检测程序支持几乎所有的注解,包括 JSR 305、Eclipse、甚至 lombok.NonNull。

import org.checkerframework.checker.nullness.qual.Nullable;

@Nullable

private Object returnNullable() {

return null;

}

public void testReturnNullable() {

Object obj = returnNullable();

// 错误:obj 可能为空

System.out.println(obj.toString());

}

Checker Framework 默认会将 @NonNull 应用到所有的函数参数和返回值上,因此,即使不添加这个注解,以下程序也是无法编译通过的:

private Object returnNonNull() {

// 错误:方法声明为 @NonNull,但返回的是 null。

return null;

}

private void argumentNonNull(Object arg) {

System.out.println(arg.toString());

}

public void testArgumentNonNull() {

// 错误:参数声明为 @NonNull,但传入的是 null。

argumentNonNull(null);

}

Checker Framework 对使用 Spring Framework 5.0 以上的用户非常有用,因为 Spring 提供了内置的空值检测注解,且能够被 Checker Framework 支持。一方面我们无需再引入额外的 Jar 包,更重要的是 Spring Framework 代码本身就使用了这些注解,这样我们在调用它的 API 时就能有效地处理空值了。举例来说,StringUtils 类里可以传入空值的函数、以及会返回空值的函数都添加了 @Nullable 注解,而未添加的方法则继承了整个框架的 @NonNull 注解,因此,下列代码中的空指针异常就可以被 Checker Framework 检测到了:

// 这是 spring-core 中定义的类和方法

public abstract class StringUtils {

// str 参数继承了全局的 @NonNull 注解

public static String capitalize(String str) {}

@Nullable

public static String getFilename(@Nullable String path) {}

}

// 错误:参数声明为 @NonNull,但传入的是 null。

StringUtils.capitalize(null);

String filename = StringUtils.getFilename("/path/to/file");

// 错误:filename 可能为空。

System.out.println(filename.length());

Optional 类型

Java 8 引入了 Optional 类型,我们可以用它来对函数的返回值进行包装。这种方式的优点是可以明确定义该方法是有可能返回空值的,因此调用方必须做好相应处理,这样也就不会引发空指针异常。但是,也不可避免地需要编写更多代码,而且会产生很多垃圾对象,增加 GC 的压力,因此在使用时需要酌情考虑。

Optional opt;

// 创建

opt = Optional.empty();

opt = Optional.of("text");

opt = Optional.ofNullable(null);

// 判断并读取

if (opt.isPresent()) {

opt.get();

}

// 默认值

opt.orElse("default");

opt.orElseGet(() -> "default");

opt.orElseThrow(() -> new NullPointerException());

// 相关操作

opt.ifPresent(value -> {

System.out.println(value);

});

opt.filter(value -> value.length() > 5);

opt.map(value -> value.trim());

opt.flatMap(value -> {

String trimmed = value.trim();

return trimmed.isEmpty() ? Optional.empty() : Optional.of(trimmed);

});

方法的链式调用很容易引发空指针异常,但如果返回值都用 Optional 包装起来,就可以用 flatMap 方法来实现安全的链式调用了:

String zipCode = getUser()

.flatMap(User::getAddress)

.flatMap(Address::getZipCode)

.orElse("");

Java 8 Stream API 同样使用了 Optional 作为返回类型:

stringList.stream().findFirst().orElse("default");

stringList.stream()

.max(Comparator.naturalOrder())

.ifPresent(System.out::println);

此外,Java 8 还针对基础类型提供了单独的 Optional 类,如 OptionalInt、OptionalDouble 等,在性能要求比较高的场景下很适用。

其它 JVM 语言中的空指针异常

Scala 语言中的 Option 类可以对标 Java 8 的 Optional。它有两个子类型,Some 表示有值,None 表示空。

val opt: Option[String] = Some("text")

opt.getOrElse("default")

除了使用 Option#isEmpty 判断,还可以使用 Scala 的模式匹配:

opt match {

case Some(text) => println(text)

case None => println("default")

Scala 的集合处理函数库非常强大,Option 则可直接作为集合进行操作,如 filer、map、以及列表解析(for-comprehension):

opt.map(_.trim).filter(_.length > 0).map(_.toUpperCase).getOrElse("DEFAULT")

val upper = for {

text

trimmed

upper 0

} yield upper

upper.getOrElse("DEFAULT")

Kotlin 使用了另一种方式,用户在定义变量时就需要明确区分 可空和不可空类型。当可空类型被使用时,就必须进行空值检测。

var a: String = "text"

a = null // 错误:无法将 null 赋值给非空 String 类型。

val b: String? = "text"

// 错误:操作可空类型时必须使用安全操作符(?.)或强制忽略(!!.)。

println(b.length)

val l: Int? = b?.length // 安全操作

b!!.length // 强制忽略,可能引发空值异常

Kotlin 的特性之一是与 Java 的可互操作性,但 Kotlin 编译器无法知晓 Java 类型是否为空,这就需要在 Java 代码中使用注解了,而 Kotlin 支持的 注解 也非常广泛。Spring Framework 5.0 起原生支持 Kotlin,其空值检测也是通过注解进行的,使得 Kotlin 可以安全地调用 Spring Framework 的所有 API。

结论

在以上这些方案中,我比较推荐使用注解来预防空指针异常,因为这种方式十分有效,对代码的侵入性也较小。所有的公共 API 都应该使用 @Nullable 和 @NonNull 进行注解,这样就能强制调用方对空指针异常进行预防,让我们的程序更为健壮。

参考资料Java NullPointerException - How to effectively handle null pointer in Java​howtodoinjava.comStatic Analysis with SpotBugs​jmri.sourceforge.netFeatures to Avoid Null Reference Exceptions in Java and Swift - DZone Java​dzone.comhttps://medium.com/@fatihcoskun/kotlin-nullable-types-vs-java-optional-988c50853692​medium.com

推荐阅读:

---------------------

作者:薄荷脑

来源:CSDN

版权声明:本文为博主原创文章,转载请附上博文链接!

java中什么是空指针异常_JAVA中的空指针异常如何处理?相关推荐

  1. java中组件与容器_java中的容器组件和非容器组件

    1.java使用到的图形类主要在java.awt 与javax.swing包中. 2.java.awt 与 javax.swing包的区别: ① java.awt中使用的图形类都是依赖于系统的图形库的 ...

  2. java boolean几个字节_Java中boolean类型到底占用多少个字节?

    1.时间:2017-07-03 07:37:06YuanMxy 2.问题描述:今天在复习java基础的时候发现一小问题,Java中boolean类型到底占用多少个字节? 3.问题解答: (1)什么是b ...

  3. java 基本类型的引用_Java中的基本数据类型与引用数据类型

    一.基本数据类型 byte.short.int.long(整数类型) float.double(浮点数类型) char(字符型) boolean(布尔类型 ) Java数据大多数存放在堆栈中.栈区:存 ...

  4. java对于数组的定义_Java中方法的定义与使用,以及对数组的初步了解。

    方法 方法的含义 定义: 方法就是用来完成解决某件事情或实现某个功能的办法. 方法实现的过程中,会包含很多条语句用于完成某些有意义的功能--通常是处理文本,控制输入或计算数值. 我们可以通过在程序代码 ...

  5. java string 后几位_java中String占几个位元组

    java中String占几个位元组以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! java中String占几个位元组 J ...

  6. java有没有回调函数_Java中的回调函数 - wangjianme的个人空间 - OSCHINA - 中文开源技术交流社区...

    Java代码的回调函数经常由框架或是系统定义,由程序开发人员填充. 它的最主要特点是即定义了调用的规范同时又非常的灵活. 回调函数有些类似于观察者模式,它们的区别在于:观察者模式返回的参数为Event ...

  7. java常见的报错_Java中常见的错误有哪些?

    原标题:Java中常见的错误有哪些? 1.java.lang.Error 错误.是所有错误的基类,用于标识严重的程序运行问题.这些问题通常描述一些不应被应用程序捕获的反常情况. 原因: 1.对系统所访 ...

  8. java i 线程不安全_java中的++i是线程安全的吗?

    java中的++i是线程安全的吗?为什么?怎么使它线程安全呢? 先说答案: 非线程安全 先说下为什么是非线程安全的? 从Java内存模型说起 Java内存模型规定了所有的便利都存储在主内存中,每个线程 ...

  9. java序列化有什么用_java中序列化的作用

    一  什么叫序列化 通俗点讲:它是处理对象流的一种机制,即可以很方便的保存内存中java对象的状态,同时也为了方便传输. 二 序列化有什么作用 1.方便传输,速度快,还很安全,被调用方序列化,调用方反 ...

  10. java原始类型和引用类型_Java中的8种原始类型

    java原始类型和引用类型 几年前,当我开始编辑Java Basics系列时,我认为将一些非常详细的信息拉到自己的帖子中是很有意义的. 这样,初学者的内容就更容易消化了. 首先,我将介绍有关Java的 ...

最新文章

  1. Set Up a Mobile Worker
  2. MATLAB 图像函数(第五章) 图像空间变换和图像配准
  3. mavenspringboot配置不同profile环境
  4. 全国计算机等级考试题库二级C操作题100套(第85套)
  5. springboot集成jsp
  6. CoreJava学习第五课 --- 进入第二阶段:面向对象编程思想
  7. 日料美食海鲜精品海报PSD分层模板,美味势不可挡
  8. 对double值进行四舍五入,保留两位小数的几种方法
  9. 买房就是创业,房东就是创业者
  10. 产品迭代的节奏怎样适应需求的变化?
  11. python里的拆包、引用、递归与匿名函数
  12. Hibernate实体类注解中如何忽略某些字段的映射
  13. mysql开源内库_king
  14. 在抖音做电商:是赚是赔,往往取决于几分钱的算计
  15. 电脑找不到wifi热点
  16. wav文件隐写:Deepsound+TIFF图片PS处理( AntCTF x D^3CTF 2022 misc BadW3ter)
  17. Win XP iis组件补丁(ghost xp)iis5.1
  18. 数据服务门槛再提升,这个“TOP1玩家”凭何再度领军?
  19. 最新最全的免费股票数据接口--沪深A股深度分析财务分析数据API接口(十一)
  20. 微信是一个计算机信息系统么,电脑微信不登录也能收到信息怎么办啊

热门文章

  1. ps在html中的应用程序,Photoshop在网页设计中的应用与方法
  2. jaeger,zipkin,datadog,skywalking等分布式追踪工具
  3. 笔记本电脑没有wifi图标无法连接无线
  4. android+apk+不被杀毒软件,大多数APK应用都会被杀毒软件报毒吗?
  5. ps,pr,3Dmax软件使用经验
  6. IEEE软件工程标准词汇表定义需求
  7. 《小狗钱钱》--chapter14~18--生活顺风顺水了起来
  8. mysql优化之 Using where; Using join buffer (Block Nested Loop) ,索引失效,检查项
  9. 通过 itms:services://? 在线安装ipa ,跨过appstore
  10. [hihoCoder#1065]全图传送