点击↑上方↑蓝色“编了个程”关注我~

每周至少一篇原创文章

这是本公众号的第 41 篇原创文章

Y说

按照惯例还是在文章开头随便聊聊。之前这个环节是借鉴的why哥,叫“荒腔走板”。现在决定还是换一个有自己特色的名字,冥思苦想,最终拍板“Y说”。

有一段时间没在公众号更新文章了,其实也不是忙,就是有点懒(主要原因),再加上没有太多灵感,所以,很抱歉~

过完年后开始复工了,该打工要打工,该更新还是要继续更新的。新年要有新气象,要努力挣钱,持续成长,也要坚持做自己想做的事。我会尽自己所能产出一些对大家有用的文章~

what is null?

为什么会想到这个话题呢?因为前段时间(大概是年前了),看到技术群里的小伙伴聊到这个话题。

想了一下,作为一个Java程序员,我在代码中经常与null打交道,时不时就会蹦出一个“空指针”异常,可以说是很熟悉了。所以在写代码的时候,也要尽量考虑到各种产生null的场景,也积累了一些小技巧。

但自己确实对null这个东西没有深究,包括群里的问题,我当时一时间也没想清楚,所以我下来偷偷地google了一把:

搞错了,应该是这个才对:

十年前的老问题了,上来就是一个夺命四连问:

  • 什么是null?

  • null是任何类型的实例吗?

  • null属于什么集合?

  • null在内存的什么地方?

我也认真读了下面的回复,总结如下:

在Java中,null「不是任何类型的实例」。你对任意的类型R,下列语句都会返回false

null instanceof R

因为null是代表“不存在”,它是一个特殊的值,所以它不包含任何类型的信息,在内存中的一个特殊的位置。所以下面两个都会输出true

System.out.println(null == null);
Animal animal = null;
List list = null;
System.out.println(animal == list);

那null到底在什么位置呢?上面那个stack overflow问题有个答主说到:

->

That is implementation specific, and you won't be able to see the representation of null in a pure Java program. (But null is represented as a zero machine address / pointer in most if not all Java implementations.)

<-

意思是这个跟Java语言无关,是由Java实现自己去决定的,但大多数实现都是把它放在了起始位置。

顺便介绍一个小tips,每个对象都有一个identityHashCode,它是用这个对象在内存中的地址返回的hash值。

我们使用System类的一个native静态方法可以打印出null的identityHashCode,发现输出是0:

System.out.println(System.identityHashCode(null));

null有害吗?

null是有害的吗?这是一个有争议的问题,我只搬运一下stack overflow上的回答。

对于编程来说,null是弊大于利的。包括null的发明者C.A.R Hoare自己都承认,当时用null只是因为它比较好实现。却没想到null带来了无尽的错误、bug、系统崩溃。原话在这:

->

「I call it my billion-dollar mistake.」 It was the invention of the null reference in 1965. At that time, I was designing the first comprehensive type system for references in an object oriented language (ALGOL W). My goal was to ensure that all use of references should be absolutely safe, with checking performed automatically by the compiler. But I couldn't resist the temptation to put in a null reference, simply because it was so easy to implement. This has led to innumerable errors, vulnerabilities, and system crashes, which have probably caused a billion dollars of pain and damage in the last forty years.

<-

但抛开程序语言不谈,我们回到面向对象的世界里面,null又是客观存在的。比如你要找一个符合xx条件的人,可这样的人就是不存在,总不能凭空捏造一个吧?

于是映射到程序语言上,我们又要解决这个问题。一般对待null有三种方式:

  1. 直接返回null

  2. 抛自定义异常

  3. 包装类

第一种是最简单粗暴的,也是我们代码中用得最多的,如果没有找到就直接返回null。这样做的弊端是在客户端需要手动检查它是否为null,不然就很容易造成不可预料的空指针异常。

这里有一个小技巧,我们可以在变量、方法参数、方法(返回值)上加注解标识它是否可能为空。这样IDE或者findBug检查就可以帮忙检查出来程序是否有潜在的空指针bug。jetbrains提供了两个注解:@Nullable@NotNull,可以配合IDEA做检查。而Spring也提供了两个注解@Nullable@NonNull。这里更推荐用Spring的,它的支持范围更大点,不受限于IDEA。Lombok好像也有相应的注解,这里不赘述。

第二种抛异常,主要用于业务上觉得不可能为空的场景。比如按某种条件去查询数据,从业务上来讲,不可能为空,那如果没有查到数据,就可以直接抛出一个异常,交给上层去catch或者往上抛。比如UserNotFoundException

第三种解决方案是使用包装类,这也是Java 8提供的Optional类的作用了。使用Optional类可以有效防止空指针异常,但在程序里面也要写相应的判断逻辑或处理逻辑,可能会增加程序的复杂性。比如ifPresent等。

但综合来看,还是更推荐第二三种解决方案。因为第一种依赖IDEA或者findBugs,不是强一致的,但是有发生空指针异常的可能性。

一些避免空指针异常的小技巧

初始化集合

在Java语言里,如果不给一个字段初始化赋值,它会被赋值为一个默认值。而除了原始类型以外,所有类型的默认值都是null,集合(List、Map、Set等)也不例外。

而在使用集合的时候,可能会忘记对它进行空指针校验,所以就很容易抛出空指针异常。

特别是在POJO类里面,使用集合是非常常见的。这个时候我们可以在声明属性的时候,给它一个初始化的值:

List<String> userIds = new ArrayList();

使用工具类

合理使用一些工具类,可以防止空指针异常的同时,还能让我们的代码看起来更优雅。

比如比较两个对象是否相等,原始的写法是调用一个对象的equals方法,但如果这个对象本身是空,就容易发生空指针异常:

// 如果a是null,下列代码会发生空指针异常
a.equals(b)

而如果改成这样就可以有效避免这个问题了:

Objects.equals(a, b)

再比如我们可能会经常判断一个集合是不是空,可能会这么写:

// 如果list为null,下列代码会抛出空指针异常
list.size() == 0;
list.isEmpty();

一些框架也提供了相应的工具类来解决这个问题,我最常用的就是apache commons的工具类CollectionUtils

CollectionUtils.isEmpty(list)

提前assert

提前assert是一种短路方案。在代码的最前面就严查对象是否为空,可以防止执行到一半抛异常,脏数据或程序的复杂性。

Java自带了assert关键字,但功能比较简单,且默认是不开启的,需要JVM参数-enableassertions或者-ea打开断言。

如果你的项目是依赖Spring的话,这里更推荐Spring提供的Assert类,功能强大,用起来也比较方便。

Assert.notNull(oil, "oil mustn't be null");
Assert.isTrue(speed > 0, "speed must be positive");

要有一颗防null的心

不要信任任何第三方接口,哪怕这个接口是你旁边的哥们写的,甚至是你自己写的。

调用其它服务的API,一定要记得对返回值做相应的空检查,这样才能让自己的程序更健壮。

在写单元测试的时候,也尽量多考虑到各种空指针的情况,防范于未然。

kotlin是如何解决的?

kotlin作为一门比较新的语言,吸取了各种语言的精华和教训,它对null做了一些限制和改进。

kotlin从语法上限制,默认不可为null。但它也没有一棍子打死,提供了允许可以为null的语法。

// 声明一个非空变量
var a: String = "hello"
// 声明一个有可能为空的变量
var b: String? = "world"var name: String? = null
// ?符号,如果变量为null,返回null,否则进行相应操作
var len = name?.length
print(len == null)  //输出:true// ?: 符号,如果坐标表达式为null,进行右边的操作
var a: String? = "hello"
var b = a?.length ?: 100  //很明显左边不为null
println(b)  //输出:5

个人觉得kotlin这种算的一种比较好的解决方案了。默认不可为null,可以有效的消除掉很多由空指针带来的隐患。但又提供语法可以支持null,通过短路返回、语法分析等手段来防止意外产生的空指针异常。

关于null就介绍到这里了,感谢大家阅读~

关于作者

我是Yasin,一个爱写博客的技术人

微信公众号:编了个程(blgcheng)

个人网站:https://yasinshaw.com

欢迎关注这个公众号

What is null?相关推荐

  1. 中国最新行政地区表(mysql)

    在网上找了很多行政地区表,但是都没有最新的mysql版本,我自己整理了一份,供xdjm们参考: DROP TABLE IF EXISTS `region`; CREATE TABLE `region` ...

  2. 2019年全国省市区代码Oracle数据库脚本

    delete from DM_GB_B_ZHRMGHGXZQHDM; insert into DM_GB_B_ZHRMGHGXZQHDM (DM, MC, SM, ZT, ZHGXSJ) values ...

  3. Access to XMLHttpRequest at file from origin ‘null‘ has been blocked by CORS policy谷歌浏览器本地打开项目js文件报错

    Access to XMLHttpRequest at 'file:///xxxxx/PQ.BaseInfo.proto' from origin 'null' has been blocked by ...

  4. 科学处理java.lang.StackOverflowError: null异常

    java.lang.StackOverflowError: null异常处理 在项目运行中出现StackOverflowError 首先要检查在编码中是否有明显的递归编码,比如死循环或者无限循环调用. ...

  5. C++ 笔记(28)— C++ 中 NULL和 nullptr 的区别

    最近看公司代码的时候发现在判断指针是否为空的时候,有的时候用的是 NULL, 有的时候用的是 nullptr 感觉很奇怪,好奇心驱使我查了下两者的区别,发现还是有很多细节需要学习的. 1. NULL ...

  6. The bean 'xxxx.FeignClientSpecification', defined in null, could not be registered. A bean with that

    错误信息: The bean 'sxbs-mall3.FeignClientSpecification', defined in null, could not be registered. A be ...

  7. [JS] undefined、null、ReferenceError的区别、变量作用域问题

    undefined.null.ReferenceError的区别 null表示"没有对象",即该处不应该有值. 典型用法是: (1) 作为函数的参数,表示该函数的参数不是对象. ( ...

  8. shell /dev/null

    /dev/null表示空设备,这里就是把日志记录到空设备里,就是不记录日志,Null 是一特殊指标值(或是一种物件参照 reference)表示这个指标并不指向任何的物件. 是一个特殊的文件,写入到它 ...

  9. 解剖JavaScript中的null和undefined

    在JavaScript开发中,被人问到:null与undefined到底有啥区别? 一时间不好回答,特别是undefined,因为这涉及到undefined的实现原理.于是,细想之后,写下本文,请各位 ...

  10. C++ 笔记(14)— 指针(指针声明、取地址、取值、new/delete、NULL指针、指针运算、指针数组、数组指针、指针传递给函数、从函数返回指针)

    1. 声明指针 指针是一个变量,其值为另一个变量的地址,即,内存位置的直接地址.就像其他变量或常量一样,您必须在使用指 针存储其他变量地址之前,对其进行声明. 指针变量声明的一般形式为: type * ...

最新文章

  1. mysql phpwind_【phpwind教程:Linux Apache php MySQL GD PHPWind 集成环境配置】-phpwind教程-【零度中国】...
  2. HP—UX更改存储设备名
  3. 使用delphi 开发多层应用(十二)使用kbmMW webserver 返回JSON
  4. MVC与单元测试实践之健身网站(八)-统计分析
  5. 话里话外:浅谈国企绩效考核问题(二)
  6. 83. 删除排序链表中的重复元素
  7. 高效数据序列化的工具 FlatBuffers 的初体验
  8. 同样的代码,为啥我的出错,调试解决问题
  9. 蓝牙小电池图标_TWS同时兼有一圈二动铁配置?国产蓝牙耳机这是要玩哪样!
  10. 多元梯度下降法--machine learning
  11. 4 软件业务分析要点
  12. Android Studio 使用魅族手机调试时,不显示 Log 的解决方法
  13. 千住焊锡丝RMA98,ESC21参数解读:金属含量、焊剂成分、质保期、Lot No. 等
  14. 云服务器win系统入站规则,云服务器win系统入站规则
  15. 重磅干货!万字长文教你如何做出 ChatGPT
  16. Linux进程5:exec族函数(execl, execlp, execle, execv, execvp, execvpe)总结及exec配合fork使用
  17. 2021-2025年中国灌溉计时器行业市场供需与战略研究报告
  18. Computing Trajectory Similarity in Linear Time: A Generic Seed-Guided Neural Metric Learning Appr...
  19. 机组配对算法matlab,基于MATLAB的风力发电机组控制算法的研究 - 北极星风力发电网...
  20. 使用labelImg遇到的问题

热门文章

  1. ESXi直通Intel网卡接口出现reset故障解决
  2. 怎么删掉流氓软件kuai8_setup
  3. DX11学习的一些资料
  4. CocosCreator发布微信子域
  5. 宝马自动驾驶战略 - 深度学习的部署【附PDF原版英文资料下载】
  6. 分享一款好用的图表制作软件,简单、美观又高效!
  7. anaconda卸载后重装出现了问题
  8. app式成语_成语大侠app下载-成语大侠下载v6.6.6.4 安卓版-西西软件下载
  9. linux 查看文件列表,linux显示文件列表命令ls,使用ls --help列出所有命令参数
  10. go 使用 mysql goup by报错