Java中避免空指针的几种方法解析
前言
Java 编程中哪个异常是你印象最深刻的,那 NullPointerException 空指针可以说是臭名昭著的。不要说初级程序员会碰到,即使是中级,专家级程序员稍不留神,就会掉入这个坑里。
Java 中任何对象都有可能为空,当我们调用空对象的方法时就会抛出 NullPointerException 空指针异常,这是一种非常常见的错误类型。我们可以使用若干种方法来避免产生这类异常,使得我们的代码更为健壮。
空指针出现的几种情况
1、调用了空对象的实例方法
public class Person { // 属性 private String name; private int age; public void print(){ System.out.println("This is Person Class"); } public String read(){ System.out.println("This is person read"); return null; } public static void main(String[] args) { Person person = null; //调用了空对象的实例方法 person.print(); } }
2、访问了空对象的属性
Person person = null; //调用了空对象的属性 System.out.println(person.age);
3、当数组是一个空对象时,取它的长度
Person person = null; //当数组是一个空对象时,取它的长度 System.out.println(person.name.length());
4、方法的返回值是 null, 调用方直接去使用
Person person = new Person(); //方法的返回值是 null, 调用方直接去使用 System.out.println(person.read().contains("Java"));
如何从根源避免空指针
1、使用之前一定要初始化,或检查是否初始化;
2、尽量避免在函数中返回 NULL, 或给出详细的注释;
3、外部传值,除非有明细的说明,否则,一定要判断是否为 NULL。
equals和equalsIgnoreCase()方法
Object类中的equals 方法在非空对象引用上实现相等关系,具有对称性 x.equals(y) 和 y.equals(x) 结果是一样的,但当x == null时会抛出空指针异常。
例如:
String x = null; String y = "world"; if(x.equals(y)){ // java.lang.NullPointerException } if(y.equals(x)){//即便 x 是 null也能避免 NullPointerException }
所以我们要把确定不为null的对象或值放在前面。
valueOf()和toString()
调用null对象的toString()会抛出空指针异常,使用valueOf()可以获得相同的值,传递一个 null 给 valueOf() 将会返回null。尤其是在那些包装类,像Integer、Float、Double和BigDecimal。
例如:
Integer i = null; System.out.println(i.toString()); // 抛出NullPointerException异常 System.out.println(String.valueOf(i)); // 返回 null不会出现异常
Optional 类型
Java 8 引入了 Optional 类型,我们可以用它来对函数的返回值进行包装。这种方式的优点是可以明确定义该方法是有可能返回空值的,因此调用方必须做好相应处理,这样也就不会引发空指针异常。但是,也不可避免地需要编写更多代码,而且会产生很多垃圾对象,增加 GC 的压力,因此在使用时需要酌情考虑。
在业务系统中,对象中嵌套对象是经常发生的场景,如下示例代码:
// 最外层对象 class Outer { Nested nested; Nested getNested() { return nested; } } // 第二层对象 class Nested { Inner inner; Inner getInner() { return inner; } } // 最底层对象 class Inner { String foo; String getFoo() { return foo; } }
业务中,假设我们需要获取 Outer 对象对底层的 Inner 中的 foo 属性,我们必须写一堆的非空校验,来防止发生 NullPointerException:
// 繁琐的代码 Outer outer = new Outer(); if (outer != null && outer.nested != null && outer.nested.inner != null) { System.out.println(outer.nested.inner.foo); }
在 Java8 中,我们有更优雅的解决方式,那就是使用 Optional是说,我们可以在一行代码中,进行流水式的 map 操作。而 map 方法内部会自动进行空校验:
Optional.of(new Outer()) .map(Outer::getNested) .map(Nested::getInner) .map(Inner::getFoo .ifPresent(System.out::println); // 如果不为空,最终输出 foo 的值
suppiler 函数自定义增强 API
我们可以利用 suppiler 函数来出一个终极解决方案:
public static <T> Optional<T> resolve(Supplier<T> resolver) { try { T result = resolver.get(); return Optional.ofNullable(result); } catch (NullPointerException e) { // 可能会抛出空指针异常,直接返回一个空的 Optional 对象 return Optional.empty(); } }
利用上面的 resolve 方法来重构上述的非空校验代码段:
Outer obj = new Outer(); // 直接调用 resolve 方法,内部做空指针的处理 resolve(() -> obj.getNested().getInner().getFoo()); .ifPresent(System.out::println); // 如果不为空,最终输出 foo 的值
使用null安全的方法和库
有很多开源库已经为您做了繁重的空指针检查工作。其中最常用的一个的是Apache commons 中的StringUtils。你可以使用StringUtils.isBlank(),isNumeric(),isWhiteSpace()以及其他的工具方法而不用担心空指针异常。
//StringUtils方法是空指针安全的,他们不会抛出空指针异常 System.out.println(StringUtils.isEmpty(null)); System.out.println(StringUtils.isBlank(null)); System.out.println(StringUtils.isNumeric(null)); System.out.println(StringUtils.isAllUpperCase(null));
输出:
true
true
false
false
断言
断言是用来检查程序的安全性的,在使用之前进行检查条件,如果不符合条件就报异常,符合就继续。
Java 中自带的断言关键字:assert,如:
assert name == null : "名称不能为空";
输出:
Exception in thread "main" java.lang.AssertionError: 名称不正确
不过默认是不启动断言检查的,需要要带上 JVM 参数:-enableassertions 才能生效。
Java 中这个用的很少,建议使用 Spring 中的,更强大,更方便好用。
Spring中的用法:
Assert.notNull(name,"名称不能为空");
创建返回空集合而不是null的方法
一个非常好的技术是创建返回一个空集合的方法,而不是一个null值。你的应用程序的代码可以遍历空集合并使用它的方法和字段,而不会抛出一个NullPointerException。Collections类提供了方便的空 List,Set 和 Map: Collections.EMPTY_LIST,Collections.EMPTY_SET,Collections.EMPTY_MAP。
例如:
public class Example { private static List<Integer> numbers = null; public static List<Integer> getList() { if (numbers == null){ return Collections.EMPTY_LIST; } else { return numbers; } } }
赋值时自动拆箱出现空指针
1、变量赋值自动拆箱出现的空指针
Long i = null; //变量赋值自动拆箱出现的空指针 long j = i;
Long 自动拆箱为 long 实质是调用了 longValue 方法,由于以上的 Long 类型变量值为 null,自动拆箱就会报 NPE。
2、方法传参时自动拆箱引起空指针异常
public static int add(int x, int y) { return x + y; } public static void main(String[] args) { Integer left = null; Integer right = null; //方法传参时自动拆箱引起空指针异常 System.out.println(add(left, right)); }
以上自动拆箱报 NPE,原因在于装箱的值为 null,调用 null 的任何 方法都会报错。
规避指南:
- 基本数据类型优于包装器类型,优先考虑使用基本类型。对于不确定的包装器类型,一定要校验是否为 NULL。
- 基本数据类型比包装器类型更加节省时间与空间。
小结
关于怎么有效避免空指针,其实还有更多,如何避免空指针,一是要注意代码编写规范,二是要提高代码素养。
到此这篇关于Java中避免空指针的几种方法解析的文章就介绍到这了,更多相关Java避免空指针内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
相关文章
Java BufferedImage转换为MultipartFile方式
这篇文章主要介绍了Java BufferedImage转换为MultipartFile方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教2021-09-09
最新评论