“只有客户发展了,才有我们的生存与发展!”这是成都创新互联公司的服务宗旨!把网站当作互联网产品,产品思维更注重全局思维、需求分析和迭代思维,在网站建设中就是为了建设一个不仅审美在线,而且实用性极高的网站。创新互联对网站制作、成都网站制作、网站制作、网站开发、网页设计、网站优化、网络推广、探索永无止境。
Java通过提供编译类型的安全性(Compile Type Safety),来保证开发人员不会错配不同的变量类型。在下面的示例中,我们试图将整形(Integer)值分配给某个字符串(String)变量,而Java会及时提醒您。
Java虽然会在编译过程中,去验证变量和赋值的类型,但是由于空值(NULL)代表了所有未初始化的对象,因此空值可以被分配为任何类型(如下图所示),且Java不会报错。
例如,Java允许如下赋值情况的出现:
这些对象在未被初始化的情况下,就指向了空引用,往往会产生Java类型的安全性漏洞。如下代码段所示,就Java而言,Null和真实对象可能并没有什么区别,但是它会导致一些不可实现的操作:
同时,由于Null属于String类型,因此在编译如下代码段时,Java甚至都不会有任何警告。
但是,我们一旦运行该程序代码,就会出现失败,并且会被提示如下的空指针异常:
空指针异常属于运行时的异常。当Java尝试去调用真实对象上的任何方法时,如果在运行时中,该对象调用的是空引用(Null Reference),那么就会抛出异常。您可以通过链接--https://dzone.com/articles/java-exceptions-1,找到有关异常、及其根源的更多详细信息。
由于种种原因,开发人员时常会忘记初始化对象和验证对象。这往往是导致空指针异常的根源。下面让我们根据上述例子,讨论如何修复NPE。
下面是一个带有Address字段的User对象。它们都可能为空。
下面是通过简单的检查(并非Null Check),来防止该问题的发生:
作为改进方案,我们可以使用Optional,并通过map函数,编写出如下类似于前例的等效语句:
与简单的Null Check相比,Optional能够再次确保我们在ifPresent lambda中使用的数据不为空。这里的再次是指:如果User或Address的确为空的话,而且ifPresent被忽略了的话,即使我们忘记了使用Optional的相关功能,它也会以突出显示.get()的方式,并提醒我们为设计提供Null Check。
其实,早在2014年,Optional就作为可选特性,在Java 1.8中被发布了。不过由于如下原因,导致其至今未能被广泛地使用:
因此,鉴于上述原因,一些开发团队会更喜欢使用Null Check,并且会用一堆逻辑性的测试覆盖率,来避免潜在的NPE。
虽然上面讨论到的Null Check与Optional的使用目的都是针对空值数据进行验证。其中,Optional还可以提醒开发者返回值为空。但是,它们无法解决隐藏在开发者头脑中的关键问题——在编译步骤中出现的疏忽与遗漏。
因此,我们需要一个解决方案,可以在编译步骤中读取代码,并通知开发人员他们可能疏漏的潜在NPE场景。对此,我们可以使用具有丰富功能的Java注释处理器(Annotation Processors)。您可以通过链接--https://www.javacodegeeks.com/2015/09/java-annotation-processors.html,了解如何使用注释处理器,来检查可变性的示例。
目前,业界有几种与NPE问题相关的注释处理器。它们并非遵循完全相同的方法。下面我们将重点讨论@NotNull和@Nullable两种注释提供工具。
Lombok(译者注:一种Java库,提供了一组非常实用的注释)的@NotNull注释可用于生成那些仅在运行时(Runtime)阻断执行的非Null Check。下面的代码段展示了该注释、及其等效语句。
检查器框架(Checker Framework)提供了@NonNull和@Nullable注释,以及可以识别潜在Null Check的编译处理器的步骤。该框架可以通过强制开发人员指定的Nullability,来发现潜在的空值。因此,您的代码必须明确声明可返回的结果为Nullable或NotNullable。下面让我们来看一个可能返回Null,而非String的简单方法:
现在,让我们使用检查器框架,来检验是否可以完成编译。
如您所见,它报出了错误,并且返回了一个未使用@Nullable注释标记的疑似空字符串。那么,让我们将其标记为@Nullable试试:
如果我们再次运行编译检查,则会得到如下错误信息:
可见,检查器框架在第19行发现了一个潜在问题,即:我们在Nullable字符串上调用了.length()。下面,让我们使用Null Check和Optional的ifPresent来予以修复:
在编译之后,我们将得到如下成功的构建信息:
至此,检查器框架向我们展示了良好的检查结果,并且突出了潜在的NPE。不过,其代价是我们必须通过@Nullable方法,标记所有可能为空的方法。为了突破该强制性的限制,我们可以创建一个带有两个字段的简单类,并且将其中一个字段标记为@NonNull:
下面是经由检查器框架的检查结果:
显然,检查器框架会强制要求我们构造一个初始化id值的构造函数,例如:
可见,检查器框架不仅能够识别潜在的NPE,而且还会迫使我们遵循特定的设计要求。这在某种程度上牺牲了框架开发的灵活性。如果您对该问题有兴趣的话,可以通过如下命令克隆我为您准备的示例:
git clone https://github.com/isicju/checker_framework_example
若要运行检查器框架的话,请使用如下命令:
Mvn clean compile
当然,检查器框架并非唯一的解决方案,Intellij Idea也提供了自己的注释--@NotNull和@Nullable,并嵌入到了IDE的插件中。目前,我尚未找到在maven编译步骤中添加它的方法。如果您对此有经验的话,欢迎您追加评论。
通过上述讨论,我们可以看到,避免空指针异常的方法可以总结为:
当然,检查器框架会给您的开发带来一些限制。在实践中,如果您必须避免使用Lombok、甚至是Builder Pattern(建造者模式)的话,我建议您基于生产环境的稳定性考虑,去使用检查器框架。
陈 峻 (Julian Chen),社区编辑,具有十多年的IT项目实施经验,善于对内外部资源与风险实施管控,专注传播网络与信息安全知识与经验;持续以博文、专题和译文等形式,分享前沿技术与新知;经常以线上、线下等方式,开展信息安全类培训与授课。
【原标题】Null Pointer Exception in Java: Causes and Ways to Avoid It(作者:Dmitry Egorov)
网站标题:细说Java中的空指针异常
文章分享:http://www.shufengxianlan.com/qtweb/news22/5922.html
网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联