Android 防止空指针异常
文章目录
- 空指针异常
- 防止空指针异常
- Java 注解
- Java 8 中的 Optional 类型
- Kotlin
- 总结
空指针异常
先复习下异常。
异常分为 Exception
和 Error
,Exception 和 Error 类都继承自Throwable
类。
- Exception(程序可恢复):表示程序可以处理的异常,可以捕获并且可恢复。遇到这类异常,应该尽可能处理异常,使程序恢复运行,而不应该随意终止异常。
- Error(程序不可恢复):一般指虚拟机相关的问题,如系统崩溃,虚拟机错误,内存空间不足,方法调用栈溢出等。这于这类错误,Java编译器不去检查也会导致应用程序中断,仅靠程序本身无法恢复和预防,遇到这样的错误,建议程序终止。
Exception 又分为运行时异常和受检查的异常:
- 运行时异常:如空指针,参数错误等。
- 受检查异常:这类异常如果没有
try/catch
语句也没有throws
抛出,编译不通过。
空指针异常(NullPointerException)属于运行时异常,当应用程序试图在需要对象的地方使用 null
时,抛出该异常。这种情况包括:
- 调用 null 对象的实例方法。
- 访问或修改 null 对象的字段。
- 将 null 作为一个数组,获得其长度。
- 将 null 作为一个数组,访问或修改其时间片。
- 将 null 作为 Throwable 值抛出。
遇到该异常,如果又没有try/catch
语句,就将导致应用程序终止。从应用程序线上问题统计中发现,空指针异常是比较常见的。所以防止空指针异常非常有必要。
防止空指针异常
要防止空指针异常,可以从三方面来入手:
- Java 注解辅助
- 引入 Java 8 中的 Optional 类型
- 使用 Kotlin 区分可空类型和非空类型
Java 注解
Java 注解可以用来表达值的可空性,帮助静态扫描工具找到可能抛出 NullPointerException
的地方。
Android 提供了可空注解@NonNull
和 非空注解@NonNull
:
@Nullablepublic Fragment findFragment(@NonNull FragmentActivity activity, @NonNull String tag) {FragmentManager fragmentManager = activity.getSupportFragmentManager();return fragmentManager.findFragmentByTag(tag);}
在使用者使用的时候,如果没有正确的使用可空类型和非空类型,就会得到编译器的提示:
所以注解只能用来提示使用者在使用的时候是可空类型还是非空类型,但并不能限定使用者传递的是可空类型还是非空类型。
Java 8 中的 Optional 类型
Java 8 中引入了 Optional
类型,它是一个可能包含或不包含非空值的容器对象,用更优雅的方式来防止 NullPointerException
。
Optional 类 提供的主要方法:
方法名字 | 表示意思 |
---|---|
of | 返回具有 Optional的当前非空值的Optional。 |
ofNullable | 返回一个 Optional指定值的Optional,如果非空,则返回一个空的 Optional 。 |
isPresent | 如果存在值,则返回 true ,否则为 false 。 |
get | 如果 Optional中存在值,则返回值,否则抛出 NoSuchElementException 。 |
map | 如果存在值,则应用提供的映射函数,如果结果不为空,则返回一个Optional结果的Optional 。 否则返回一个空的Optional 。 |
flatMap | 如果一个值存在,应用提供的Optional映射函数给它,返回该结果,否则返回一个空的Optional 。 这种方法类似于map(Function) ,但是提供的映射器是一个结果已经是Optional映射器,如果被调用, flatMap不会用额外的Optional 。 |
orElse | 返回值如果存在,否则返回 other 。 |
测试例子:
创建一个User类:
public class User {private String nickname;private Address address;public User() {}public Optional<Address> getAddress() {return Optional.ofNullable(address);}public void setAddress(Address address) {this.address = address;}public String getNickname() {return nickname;}public void setNickname(String nickname) {this.nickname = nickname;}}
获取 Nickname:
private String getUserNickname(User user) {return Optional.ofNullable(user).map(new Function<User, String>() {@Overridepublic String apply(User t) {return t.getNickname();}}).orElse("default");}String nickname = getUserNickname(null);//运行结果:nickname="default"
ofNullable
方法主要将对象包装成一个Optional
对象(用的就是组合):
private static final Optional<?> EMPTY = new Optional<>();private final T value;private Optional() {this.value = null;}public static<T> Optional<T> empty() {@SuppressWarnings("unchecked")Optional<T> t = (Optional<T>) EMPTY;return t;}private Optional(T value) {this.value = Objects.requireNonNull(value);}public static <T> Optional<T> of(T value) {return new Optional<>(value);}public static <T> Optional<T> ofNullable(T value) {return value == null ? empty() : of(value);}
map
方法会检测当前值是否为空,如果为空就返回一个空的Optional
对象,否则将接口Function
的回调方法apply
的返回值包装成一个新的Optional
对象。
public boolean isPresent() {return value != null;}public <U> Optional<U> map(Function<? super T, ? extends U> mapper) {Objects.requireNonNull(mapper);if (!isPresent()) {return empty();} else {return Optional.ofNullable(mapper.apply(value));}}
orElse
方法检测当前值是否为空,如果为空,就返回传入的默认值:
public T orElse(T other) {return value != null ? value : other;}
从上面可以 看出,使用 Optional
类,主要是将对象包装成 Optional
对象来防止 NullPointerException
。
Optional
类 还可以解决在实际开过程中,经常遇到的一些链式调用引起的NullPointerException
。
下面通过User 类,获取 Country 类 的属性,一般会这样写:
String countryName = null;Address address = user.getAddress();if(address!=null) {Country country = address.getCountry();if(country !=null) {countryName = country.getName();}}
上面代码中不得不对过程中遇到的每一个对象做判空处理,如果调用链很长,代码就会越不友好。修改user.getAddress()
和 address.getCountry()
方法的返回值:
public Optional<Address> getAddress() {return Optional.ofNullable(address);}
public Optional<Country> getCountry() {return Optional.ofNullable(country);}
使用Optional
类 ,将链式调用中遇到的任何一个对象都包装成 Optional
对象:
private String getCountryName(User user) {return Optional.ofNullable(user).flatMap(new Function<User, Optional<Address>>() {@Overridepublic Optional<Address> apply(User t) {return t.getAddress();}}).flatMap(new Function<Address, Optional<Country>>() {@Overridepublic Optional<Country> apply(Address t) {return t.getCountry();}}).map(new Function<Country, String>() {@Overridepublic String apply(Country t) {return t.getName();}}).orElse("default");}String countryName = getCountryName(user);//运行结果:countryName = "default"
在上面链式方法调用中,中间如果有任何一个对象为空,都不可能抛出空指针异常。
虽然 Optional
可以防止NullPointerException
,但是代码还是很冗长,并且额外的包装接口还会影响运行时的性能。
Kotlin
Kotlin 区分可空类型和非空类型,并会在编译期间对可空类型和非空类型检查。
可空类型,问号添加在类型的后面表示这个类型的变量可以存储 null
引用:
fun findFragment(activity: FragmentActivity?, tag: String?): Fragment? {val fragmentManager = activity?.supportFragmentManagerreturn fragmentManager?.findFragmentByTag(tag)}
如果没有正确使用,将得到编译器的错误提示,并无法编译:
非空类型,没有问号添加在类型的后面表示这个类型的变量不可以存储 null
引用:
fun findFragment(activity: FragmentActivity, tag: String): Fragment? {val fragmentManager = activity.supportFragmentManagerreturn fragmentManager?.findFragmentByTag(tag)}
同样,如果没有正确使用,将得到编译器的错误提示,并无法编译:
可空类型和非空类型在运行时并没有什么区别,只是在编译期间对类型进行检查。所以,使用Kotlin 的可空类型并不会在运行时带来额外的开销。
对于可空类型的判断,Kotlin 还提供了安全调用运算符?.
,Elvis 运算符?:
,安全转换符as?
,非空断言!!
来防止空指针异常,这里不再一一介绍。
总结
防止空指针异常,有三种方法:使用Java 注解辅助,引入 Java 8 中的 Optional 类型, 使用 Kotlin 区分可空类型和非空类型。使用Java 注解辅助,只能提示使用者在使用的时候是可空类型还是非空类型,并不能限定使用者传递的是可空类型还是非空类型;引入 Java 8 中的 Optional 类型,能防止空指针异常,但是代码还是很冗长,并且额外的包装接口还会影响运行时的性能;使用 Kotlin 区分可空类型和非空类型,会在编译期间对类型进行检查,并且不会在运行时带来额外的开销。总之,使用 Kotlin 是防止空指针异常的最好方式。
Android 防止空指针异常相关推荐
- android单选按钮空值,Android的 - 空指针异常的对话与单选按钮
谁能帮我这个,告诉我,我做了错误...当我尝试用单选按钮的Eclipse做任何事情我抛出该异常Android的 - 空指针异常的对话与单选按钮 threadid=1: thread exiting w ...
- Android Service Security
0x00 科普 一个Service是没有界面且能长时间运行于后台的应用组件.其它应用的组件可以启动一个服务运行于后台,即使用户切换到另一个应用也会继续运行.另外,一个组件可以绑定到一个service来 ...
- android 拍照空指针,空指针异常时嵌入照片中的Android
分贝的android我是做一个应用程序在SQLite数据库中插入数据和ii有一个空指针异常时记录添加到数据库空指针异常时嵌入照片中的Android 这个初学者代码得到的ImageView的PIC,并将 ...
- Android NullPointerException解决方法(空指针异常)
不知道你在开发中遇到Android空指针异常NullPointerException相关的问题,这类问题的可能性比较多,常规的错误如下 一.刚刚升级了Android SDK,没有对emulator做清 ...
- android 数组指针异常,Android JSON解析Json数组是[]在解析时抛出空指针异常,如何以正确的方式写入?...
我有复杂的API,我解析并显示在列表视图中,我将努力解析JSONArray.Here我将在斗争之后Json数组这是在帖子json对象"tags_name":["Activ ...
- android java 指针,opencv android:向我的代码中添加cascade分类器后出现空指针异常
我在casecadeclassifier.java类中收到空指针异常 在这里: Mat objects_mat = objects; detectMultiScale_4(nativeObj, ima ...
- android getdecorview 出现空指针,android – 为什么我从TabWidget得到一个空指针异常?...
我正在编写一个android程序,其中我有一个使用制表符的活动. 活动 public class UnitActivity extends TabActivity { @Override public ...
- Android MVP Presenter 中引发的空指针异常
Android MVP Presenter 中引发的空指针异常 参考文章: (1)Android MVP Presenter 中引发的空指针异常 (2)https://www.cnblogs.com/ ...
- android内存溢出错误,Android Studio 生成 JavaDoc 空指针异常|文档编码出错|内存溢出...
一般使用Android Studio生成 JavaDoc会有三个问题: 1.空指针异常 Tools --> Generate JavaDoc -->打开对话框活,在"Other ...
最新文章
- 用createinstallmedia创建可恢复的OSX安装DMG
- JavaScript之childNodes属性、nodeType属性学习
- k8s php mysql_在k8s上部署第一个php应用
- SAP Commerce Cloud WCMS 里的 home 页面和 SAP Spartacus Page API 返回的数据比较
- spring源码分析第四天------springmvc核心原理及源码分析
- 那些公司用计算机仪表电缆,计算机及仪表用电缆技术
- CSS 框的外观 outline属性
- LeetCode 437. 路径总和 III
- iOS入门培训还要钱?看博客,看视频都拿下
- VB中PictureBox控件使用教程
- 安卓电子书格式_不用电脑,6招教你把手机上的电子书传输到Kindle上
- SSO单点登录方案大全
- 真人语音朗读软件_讯飞语音云助力移动“和阅读”,打造个性化听书应用
- 计算机加密技术图片,基于Henon映射的图像加密技术
- 使用zii.widgets.CDetailView显示内容
- strapi终于装好了,网速太慢了,处理了一下代理,新建了一个.zshrc文件,加入了pon和poff两个函数
- 黑马程序员C++学习-01
- 蛰伏四年,他才是满帮成功上市背后的关键先生
- 陆琪众筹出书:一种读者视野的重新诠释_娱乐频道_红网
- 极客日报:雷军吐槽友商“PPT首发”,联想高管反击;iPhone 6 Plus成过时产品;IPython 7.30正式发布