java中的空指针异常怎么解决

如题所述

原文:https://www.zhihu.com/question

你这个问题的解决

问题定位:

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

问题解决:

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

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 <T> List<T> 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 加入到项目依赖中,以便使用对应的注解。

    <dependency>
       <groupId>com.github.spotbugs</groupId>
       <artifactId>spotbugs-annotations</artifactId>
       <version>3.1.7</version>
    </dependency>

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

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

    Optional<String> 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 <- opt
     trimmed <- Some(text.trim())
     upper <- Some(trimmed) if trimmed.length > 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 进行注解,这样就能强制调用方对空指针异常进行预防,让我们的程序更为健壮。

温馨提示:内容为网友见解,仅供参考
第1个回答  推荐于2017-09-24
空指针异常产生的主要原因如下:itjob网上有例子,
(1)当一个对象不存在时又调用其方法会产生异常obj.method() // obj对象不存在
(2)当访问或修改一个对象不存在的字段时会产生异常obj.method() // method方法不存在
(3)字符串变量未初始化;
(4)接口类型的对象没有用具体的类初始化,比如:
List lt;会报错
List lt = new ArrayList();则不会报错了
当一个对象的值为空时,你没有判断为空的情况。你可以试着把下面的代码前加一行代码:
if(rb!=null && rb!="")
改成:
if(rb==null);
if(rb!==null&&rb!="") 或者if((“”).equals(rb))

空指针的解决办法:
重点关注报错发生的所在行,通过空指针异常产生的两条主要原因诊断具体的错误。同时为了避免空指针的发生,最好在做判断处理时将“null”或者空值放于设定的值之前。本回答被提问者采纳
第2个回答  2016-12-13
找到异常位置,处理null,可以加if等

系统发生空指针异常怎么解决
总的来说,解决空指针异常需要综合运用调试技巧、代码审查和测试方法。通过精确地定位问题、深入分析原因、采取恰当的修复措施,并进行全面的测试,可以有效地解决空指针异常并确保系统的稳定性。

java中的空指针异常如何处理?
综上所述,采用注解进行空值检测是较为推荐的方法,它在代码中注入了预防逻辑,同时对代码的侵入性较小。所有公共 API 应使用 `@Nullable` 和 `@NonNull` 进行注解,这样可以强制调用方预防空指针异常,提升程序的健壮性。

java报错null pointer exception是什么意思?如何解决?
这个是空指针异常,空指针的解决办法: 重点关注报错发生的所在行,通过空指针异常产生的两条主要原因诊断具体的错误。同时为了避免空指针的发生,最好在做判断处理时将“null”或者空值放于设定的值之前。在Java中一般报空指针异常的原因有以下几种:1、字符串变量未初始化;2、接口类型的对象没有用具体...

空指针异常怎么解决
1、空指针异常抛出的异常信息一般是:Exception in thread "main" java.lang.NullPointerException at xx.Test.tt(Test.java:7)at xx.Test.main(Test.java:17)从异常信息中可以看出具体在那个类的哪一行,比如我的在第7行。2、然后我们可以查看代码中的第7行。3、从上述图中,我们明显可以看到第7...

为啥出现空指针异常啊?
当我们遇到bug时,保持冷静是非常重要的。只有冷静,我们才能更清晰地思考问题,从而找到解决问题的方法。接下来,我将以空指针异常为例,一步步引导大家如何进行分析和解决。在Java开发中,空指针异常(NullPointerException)是常见的错误之一,它通常在我们尝试调用对象的方法或属性时出现,而该对象实际上是...

java.lang.NullPointerException错误是什么意思
在Java中一般报空指针异常的原因有以下几种:1、字符串变量未初始化;2、接口类型的对象没有用具体的类初始化,比如:List lt;会报错 List lt = new ArrayList();则不会报错了 3、当一个对象的值为空时,没有判断为空的情况。可以试着把下面的代码前加一行代码:if(rb!=null && rb!="")...

matlab报错java.lang.nullpointerexception?
针对初学者常见的Java NullPointerException 空指针异常,本文将详细解析异常现象、原因与解决办法。首先,我们通过一段代码示例,直观展示 NPE 的运行结果,为后续分析打下基础。了解了异常现象,接下来深入分析 NPE 产生的原因。通常,NPE 出现于使用了未初始化或未赋值的变量,特别是在该变量被调用方法或...

java中的空指针异常怎么解决
问题解决: 对一个空对象调用里面的方法或者属性的时候会报空指针,检查这个对象为什么是空即可。 Java 空指针异常的若干解决方案 Java 中任何对象都有可能为空,当我们调用空对象的方法时就会抛出 NullPointerException 空指针异常,这是一种非常常见的错误类型。我们可以使用若干种方法来避免产生这类异常,使得我们的代码...

java.lang.nullpointerexception错误是什么意思
三、解决方式 1. 检查对象实例:在调用对象的方法或访问其属性之前,确保该对象已经被正确初始化并非null。2. 使用条件判断:在调用方法或访问属性之前,通过条件判断确保对象不为null。3. 使用Java 8的Optional类:Optional类可以帮助我们更优雅地处理可能为null的情况,避免空指针异常。4. 使用debug工具...

如何处理Java. lang. NullPointerException异常?
第一行给出了是异常的原因,NullPointerException,这相当于c和c++里的空指针,所以必须处理,否则程序运行时会出错。接下来的几行给出了出错的程序调用,指出了Main.java的11行出现java.lang.NullPointerException异常,但是具体的原因还得从调用的代码上去查找,这里是Main.java的第7行传递了null导致的。

相似回答