What is null?

简介: 按照惯例还是在文章开头随便聊聊。之前这个环节是借鉴的why哥,叫“荒腔走板”。现在决定还是换一个有自己特色的名字,冥思苦想,最终拍板“Y说”。有一段时间没在公众号更新文章了,其实也不是忙,就是有点懒(主要原因),再加上没有太多灵感,所以,很抱歉~

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就介绍到这里了,感谢大家阅读~

目录
相关文章
|
3月前
|
存储 网络协议 算法
ISIS协议详解
ISIS协议是一种链路状态路由协议,广泛用于大规模网络中。它最初基于OSI模型设计,后经扩展支持TCP/IP协议。ISIS通过SPF算法计算最短路径,使用NSAP地址进行设备寻址,具备灵活的区域划分和分层结构。协议通过L1、L2及L1/2路由器实现区域内部与骨干区域的通信,支持P2P和广播网络类型,具备邻接建立、LSDB同步、路由计算等核心机制,广泛应用于运营商和企业骨干网中。
|
5月前
|
敏捷开发 数据可视化 BI
敏捷开发轻量级看板工具:提升效率的秘密武器
敏捷开发是一种以人为核心、迭代递进的软件开发方法,强调灵活应对变化与持续反馈。其核心原则来自《敏捷宣言》,包括重视个体协作、可工作软件、客户合作和响应变化。与传统开发相比,敏捷更注重快速交付和客户参与。看板作为轻量级工具,通过可视化工作流提升协作效率,适合Scrum等敏捷方法。主流工具如板栗看板、KanbanFlow等提供简洁界面和实时协作功能,帮助团队管理任务流程。敏捷团队应结合看板工具进行日常站会、Sprint回顾,持续优化开发流程。轻量级看板工具适用于不同规模团队,是提升敏捷效率的有效选择。
428 0
|
7月前
|
人工智能 算法 安全
AI时代:不可替代的“人类+”职业技能
在生成式人工智能快速发展的背景下,关于“人类工作者是否会被算法取代”的焦虑日益增加。本文探讨了AI对职业的重塑作用,指出真正的挑战在于如何通过职业技能培训重新定义人类的不可替代性。文章分析了替代与创造的辩证关系,强调人机协作时代的核心能力,如架构设计力、情感智慧和伦理决策力,并提出职业技能培训应从岗位技能导向转向能力生态构建。最终,通过系统性培训发展“人类+”特质,使AI成为解放人类潜能的工具,而非竞争对手。
|
11月前
|
移动开发 HTML5
HTML5 SVG实现可爱的小鸟卡通动画3D特效
HTML5 SVG实现可爱的小鸟卡通动画
176 29
|
12月前
|
自然语言处理 搜索推荐 数据安全/隐私保护
鸿蒙登录页面好看的样式设计-HarmonyOS应用开发实战与ArkTS代码解析【HarmonyOS 5.0(Next)】
鸿蒙登录页面设计展示了 HarmonyOS 5.0(Next)的未来美学理念,结合科技与艺术,为用户带来视觉盛宴。该页面使用 ArkTS 开发,支持个性化定制和无缝智能设备连接。代码解析涵盖了声明式 UI、状态管理、事件处理及路由导航等关键概念,帮助开发者快速上手 HarmonyOS 应用开发。通过这段代码,开发者可以了解如何构建交互式界面并实现跨设备协同工作,推动智能生态的发展。
705 10
鸿蒙登录页面好看的样式设计-HarmonyOS应用开发实战与ArkTS代码解析【HarmonyOS 5.0(Next)】
|
11月前
|
人工智能 算法 UED
《鸿蒙Next人工智能翻译:模型轻量化的用户体验变革》
在鸿蒙Next生态中,人工智能翻译应用的模型轻量化正悄然变革用户体验。它通过提升响应速度、降低资源占用、节省能耗、提高多设备适配性和便于更新迭代,使翻译服务更高效流畅。轻量化模型减少了不必要的参数和计算量,实现即时反馈,保障系统稳定运行,延长设备续航时间,并确保多设备体验一致,为用户带来更加便捷、优质的跨语言交流体验。
257 3
|
10月前
|
安全 网络安全 数据中心
服务器托管适用什么场景?
在数字化时代,服务器托管对企业运营至关重要。本文解析了五种常见托管方式:共享托管经济实惠,适合小型网站;VPS灵活可控,适合中小型企业;专用服务器性能卓越,适合大型应用;云托管灵活扩展,适应现代需求;托管式服务外包管理,省心省力。选择时需综合考虑预算、技术能力及性能要求,找到最佳解决方案。
403 0
|
机器学习/深度学习 人工智能 数据可视化
小滑块上个斜面,难倒多少高中生?现在,AI让它动起来了
《Augmented Physics:基于机器学习的物理学习工具》 高中物理学习中,小滑块上斜面等问题常让学生困惑。Augmented Physics利用AI技术,将静态物理图示转化为交互式模拟,通过增强实验、动画图示、双向操作和参数可视化等技术,帮助学生直观理解物理概念。研究表明,该工具能有效提升学生对物理概念的理解,具备广阔的应用前景。
281 1
|
网络协议
逆向学习网络篇:心跳包与TCP服务器
逆向学习网络篇:心跳包与TCP服务器
606 0
|
SQL 关系型数据库 MySQL
OBCP实践 - 迁移 MySQL 数据到 OceanBase 集群
OBCP实践 - 迁移MySQL数据到OceanBase集群,这是一个涉及到将现有MySQL数据库的数据和表结构迁移到OceanBase分布式数据库集群的实际操作过程。OceanBase是一款高度兼容MySQL协议的分布式数据库产品,支持在线平滑迁移,以便企业用户可以从传统的MySQL数据库平滑迁移到OceanBase,以实现更高的可用性、扩展性和性能。
426 0