A. java中的空指针异常怎么解决
原文:https://www.hu.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");
// 若找不到该条记录,则抛出
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 原生支持的空值检测注解;
e.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 加入到项目依赖中,以便使用对应的注解。
以下是对不同使用场景的说明:
对于 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。
Checker Framework 默认会将 @NonNull 应用到所有的函数参数和返回值上,因此,即使不添加这个注解,以下程序也是无法编译通过的:
Checker Framework 对使用 Spring Framework 5.0 以上的用户非常有用,因为 Spring 提供了内置的空值检测注解,且能够被 Checker Framework 支持。一方面我们无需再引入额外的 Jar 包,更重要的是 Spring Framework 代码本身就使用了这些注解,这样我们在调用它的 API 时就能有效地处理空值了。举例来说,StringUtils 类里可以传入空值的函数、以及会返回空值的函数都添加了 @Nullable 注解,而未添加的方法则继承了整个框架的 @NonNull 注解,因此,下列代码中的空指针异常就可以被 Checker Framework 检测到了:
Optional 类型
Java 8 引入了 Optional<T> 类型,我们可以用它来对函数的返回值进行包装。这种方式的优点是可以明确定义该方法是有可能返回空值的,因此调用方必须做好相应处理,这样也就不会引发空指针异常。但是,也不可避免地需要编写更多代码,而且会产生很多垃圾对象,增加 GC 的压力,因此在使用时需要酌情考虑。
方法的链式调用很容易引发空指针异常,但如果返回值都用 Optional 包装起来,就可以用 flatMap 方法来实现安全的链式调用了:
Java 8 Stream API 同样使用了 Optional 作为返回类型:
此外,Java 8 还针对基础类型提供了单独的 Optional 类,如 OptionalInt、OptionalDouble 等,在性能要求比较高的场景下很适用。
其它 JVM 语言中的空指针异常
Scala 语言中的 Option 类可以对标 Java 8 的 Optional。它有两个子类型,Some 表示有值,None 表示空。
除了使用 Option#isEmpty 判断,还可以使用 Scala 的模式匹配:
Scala 的集合处理函数库非常强大,Option 则可直接作为集合进行操作,如 filer、map、以及列表解析(for-comprehension):
Kotlin 使用了另一种方式,用户在定义变量时就需要明确区分 可空和不可空类型。当可空类型被使用时,就必须进行空值检测。
Kotlin 的特性之一是与 Java 的可互操作性,但 Kotlin 编译器无法知晓 Java 类型是否为空,这就需要在 Java 代码中使用注解了,而 Kotlin 支持的 注解 也非常广泛。Spring Framework 5.0 起原生支持 Kotlin,其空值检测也是通过注解进行的,使得 Kotlin 可以安全地调用 Spring Framework 的所有 API。
结论
在以上这些方案中,我比较推荐使用注解来预防空指针异常,因为这种方式十分有效,对代码的侵入性也较小。所有的公共 API 都应该使用 @Nullable 和 @NonNull 进行注解,这样就能强制调用方对空指针异常进行预防,让我们的程序更为健壮。
B. 如何在Android Studio中使用java 8
如果你首先是一个Java开发人员,你可能认为Android将完全支持当前版本的Java是公理的。你会错了。部分原因是由于Oracle的诉讼,部分原因是它不是他们的首要任务,Android在采用当前的Java SE版本时速度很慢。使用Android Nougat(7.0),Android团队已开始转向Java(Java 8)的当前版本,但部分和增量。好消息是,Android库的许多Java兼容部分显然是基于来自OpenJDK 8的源代码,因此令人讨厌的小不一致的数量应该随着时间的推移而减少。注意,为了编译Android
Nougat,您必须安装Java 8 SDK,即使您没有使用任何这些功能!
“Jack”(Java编译工具包?)是一个新的工具链,它将Java代码编译成自己的格式 - 没有更多的类文件到Dex翻译。这应该更快,但也意味着工具像findbugs读取.class文件将不再工作,没有额外的编译步骤。
要使用Jack,您必须具有Android Studio 2.1或更高版本以及Build Tools软件包24或更高版本。并且必须在build.gradle或模块设置中将Source和Target编译级别设置为1.8。另外,从当前预览,您必须手动添加jackOptions使能条目到build.gradle,在defaultConfig条目内:
android {
defaultConfig {
// Other defaults here...
// Enable 'jack'
jackOptions {
enabled true
}
}
// And if you prefer to set these manually:
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
C. 如何提高 Android 代码质量
(1)插件安装
在Android Studio中选择Preferences -> Plugins,输入查找findBugs进行插件安装。
(2)插件使用
在build.gradle文件中,按照下面步骤进行设置:
添加plugin apply plugin:’findbugs’
定义任务,指定输出格式
这里要注意因为findBugs是检查class文件,所以在定义task的时候,是dependsOn: “assembleDebug”,确保运行findbugs的task能够成功检测。
通过gradle findbugs方式,在工程目录下运行命令,检测完成后,会在制定的目录下生成报告文档。文档支持xml和html两种格式,本文设置的是html格式,可以直接用浏览器打开。
当然,和lint一样,findBugs也支持手动检测的方式。
在工程里,右键 FindBugs -> (选择检测的范围)。检测完之后,底部工具栏会跳到FindBugs-IEDA下。
3
PMD
PMD是一个很有用的工具,它跟Findbugs类似,但是它不是检测字节码,它是直接检测源代码。它使用静态分析来发现错误。
为什么要将它们同时使用呢?因为它们的检测方法不同,可以取到互补的作用。
检测范围
可能的bug——空的try/catch/finally/switch块。
无用代码(Dead code):无用的本地变量,方法参数和私有方法。
空的if/while语句。
过度复杂的表达式——不必要的if语句,本来可以用while循环但是却用了for循环。
可优化的代码:浪费性能的String/StringBuffer的使用。
(1)插件安装
同样可以通过AS的plugin进行安装,推荐安装QAPlug-PMD。
(2)插件使用
在build.gradle文件中进行如下配置
导入Plugin:apply plugin: ‘pmd’
Task配置
4
CheckStyles
这个工具用来自动检测java源码。启动之后,可以按照制定的规则对java源码进行检查,被将所有的不符合规范的地方生成报告通知给你。
检测范围
注解
javadoc注释
命名规范
文件头
导入包规范
尺寸设置
空格
正则表达式
修饰符
代码块
编码问题
类设计问题
重复、度量以及一些杂项
总而言之,是一些代码规范问题!!
(1)插件安装
通过AS的Plugin进行安装
(2)插件使用
导入Plugin
apply plugin: ‘checkstyle’
设置CheckStyle的版本
checkstyle {
toolVersion ‘6.1.1’
showViolations true
}
配置任务
5
插件的接入与使用
检测范围
lint、PMD、findBugs和CheckStyle检测范围之和。
(1)插件安装
下载整合插件的文件包(文末),和app工程目录同级放置。
在app的build.gradle文件导入整合插件脚本
apply from: ‘../config/quality.gradle’
(2)插件使用
修改quality.gradle 的appDir字段,设置检测的工程目录
// 应用目录名称def appDir = “app-k12”
进入工程根目录,运行gradle check,检测完成后,会在build/reports下生成相应的检测报告文件。当然也可以按照每个插件的使用方式单独使用。
D. 有什么好用的Android Studio的插件值得推荐
1.GsonFormat
快速将json字符串转换成一个Java Bean,免去我们根据json字符串手写对应Java Bean的过程。
使用方法:快捷键Alt+S也可以使用Alt+Insert选择GsonFormat
2.Android ButterKnife Zelezny
配合ButterKnife实现注解,从此不用写findViewById,想着就爽啊。在Activity,Fragment,Adapter中选中布局xml的资源id自动生成butterknife注解。
使用方法:Ctrl+Shift+B选择图上所示选项
3.Android Code Generator
根据布局文件快速生成对应的Activity,Fragment,Adapter,Menu。
4.Android Parcelable code generator
JavaBean序列化,快速实现Parcelable接口。
5.Android Methods Count
显示依赖库中得方法数
6.Lifecycle Sorter
可以根据Activity或者fragment的生命周期对其生命周期方法位置进行先后排序,快捷键Ctrl + alt + K
7.CodeGlance
在右边可以预览代码,实现快速定位
8.findBugs-IDEA
查找bug的插件,Android Studio也提供了代码审查的功能(Analyze-Inspect Code…)
9.ADB WIFI
使用wifi无线调试你的app,无需root权限
也可参考以下文章:
Android wifi无线调试App新玩法ADB WIFI
10.AndroidPixelDimenGenerator
Android Studio自动生成dimen.xml文件插件
11.JsonOnlineViewer
在Android Studio中请求、调试接口
12.Android Styler
根据xml自动生成style代码的插件
Usage:
a. lines with future style from your layout.xml file
b. paste it to styles.xml file with Ctrl+Shift+D (or context menu)
c. enter name of new style in the modal window
d. your style is prepared!
13.Android Drawable Importer
这是一个非常强大的图片导入插件。它导入Android图标与Material图标的Drawable ,批量导入Drawable ,多源导入Drawable(即导入某张图片各种dpi对应的图片)
14.SelectorChapek for Android
通过资源文件命名自动生成Selector文件。
15.GenerateSerialVersionUID
实现Serializable序列化bean
Adds a new action ‘SerialVersionUID’ in the generate menu (alt + ins). The action adds an serialVersionUID field in the current class or updates it if it already exists, and assigns it the same value the standard ‘serialver’ JDK tool would return. The action is only visible when IDEA is not rebuilding its indexes, the class is serializable and either no serialVersionUID field exists or its value is different from the one the ‘serialver’ tool would return.
16.genymotion
速度较快的android模拟器
17.LeakCanary
帮助你在开发阶段方便的检测出内存泄露的问题,使用起来更简单方便。
可以参考以下文章:
LeakCanary 中文使用说明
18.Android Postfix Completion
可根据后缀快速完成代码,这个属于拓展吧,系统已经有这些功能,如sout、notnull等,这个插件在原有的基础上增添了一些新的功能,我更想做的是通过原作者的代码自己定制功能,那就更爽了
19.Android Holo Colors Generator
通过自定义Holo主题颜色生成对应的Drawable和布局文件
20.dagger-intellij-plugin
dagger可视化辅助工具
21.
maven gradle 依赖支持自动补全
22.RemoveButterKnife
ButterKnife这个第三方库每次更新之后,绑定view的注解都会改变,从bind,到inject,再到bindview,搞得很多人都不敢升级,一旦升级,就会有巨量的代码需要手动修改,非常痛苦
当我们有一些非常棒的代码需要拿到其他项目使用,但是我们发现,那个项目对第三方库的使用是有限制的,我们不能使用butterknife,这时候,我们又得从注解改回findviewbyid
针对上面的两种情况,如果view比较少还好说,如果有几十个view,那么我们一个个的手动删除注解,写findviewbyid语句,简直是一场噩梦(别问我为什么知道这是噩梦)
所以,这种有规律又重复简单的工作为什么不能用一个插件来实现呢?于是RemoveButterKnife的想法就出现了。
具体介绍
23.AndroidProguardPlugin
一键生成项目混淆代码插件,值得你安装~(不过目前可能有些第三方项目的混淆还未添加完全)
24.otto-intellij-plugin
otto事件导航工具。
25.eventbus-intellij-plugin
eventbus导航插件
26.idea-markdown
markdown插件
27.Sexy Editor
设置AS代码编辑区的背景图
首先点击界面的设置按钮 进入设置界面,选中Plugins,右边选择 Browser … ,输入Sexy … 下面自动弹出候选插件,右边点击Install 安装
安装成功 后需要重启AS
重启完成之后 进入设置界面 选择other Setting 下的Sexy Editor , 右侧 insert 一张或多张图片即可,上面的其他设置可以设置方位 间隔时间 透明度等等,设置完成后,要关闭打开的文件,重新打开项目文件即可在代码编辑区显示插入的图片,作为代码编辑区的背景图。
28.folding-plugin
布局文件分组的插件
29.Android-DPI-Calculator
DPI计算插件
使用:
或者
30.gradle-retrolambda
在java 6 7中使用 lambda表达式插件
修改编译的jdk为java8:
31.Android Studio Prettify
可以将代码中的字符串写在string.xml文件中
选中字符串鼠标右键选择图中所示
这个插件还可以自动书写findViewById
32.Material Theme UI
添加Material主题到你的AS
33..ignore
我 们都知道在Git 中想要过滤掉一些不想提交的文件,可以把相应的文件添加到.gitignore 中,而.gitignore 这个Android Studio 插件根据不同的语言来选择模板,就不用自己在费事添加一些文件了,而且还有自动补全功能,过滤文件再也不要复制文件名了。我们做项目的时候,并不是所有文 件都是要提交的,比如构建的build 文件夹,本地配置文件,每个Mole 生成的iml 文件,但是我们每次add,commit 都会不小心把它们添加上去,而gitignore 就是解决这种痛点的,如果你不想提交的文件,就可以在创建项目的时候将这个文件中添加即可,将一些通用的东西屏蔽掉。
34.CheckStyle-IDEA
CheckStyle-IDEA 是一个检查代码风格的插件,比如像命名约定,Javadoc,类设计等方面进行代码规范和风格的检查,你们可以遵从像Google Oracle 的Java 代码指南 ,当然也可以按照自己的规则来设置配置文件,从而有效约束你自己更好地遵循代码编写规范。
35.Markdown Navigator
github:Markdown Navigator
Markdown插件
36.ECTranslation
Android Studio Plugin,Translate English to Chinese. Android Studio 翻译插件,可以将英文翻译为中文。
37.PermissionsDispatcher plugin
github:PermissionsDispatcher plugin
自动生成6.0权限的代码
38.WakaTime
github:WakaTime
记录你在IDE上的工作时间
39.AndroidWiFiADB
无线调试应用
40.AndroidLocalizationer
可用于将项目中的 string 资源自动翻译为其他语言的 Android Studio/IntelliJ IDEA 插件
欢迎关注微信公众号:终端研发部。和我一块交流和学习