null是什么

null是什么

我们会围绕这几个问题进行探讨

  1. null属于什么类型
  2. null是某个具体的实例么
  3. null是什么
  4. null在内存中怎么表示
    我们从null属于什么类型说起

在Java Language Specification chapter4–Types, Values, and Variables中有介绍.
java中数据有两种类型,基本类型和引用类型,基本类型又分NumericType和boolean,NumericType就包括我们常说的整型(int,long,short,byte,char)以及浮点型(float,double)。那么除了这两种类型之外,JLS里着重说了null这种特殊的类型。

There is also a special null type, the type of the expression null (§3.10.7, §15.8.1), which has no name.
Because the null type has no name, it is impossible to declare a variable of the null type or to cast to the null type.
The null reference is the only possible value of an expression of null type.
The null reference can always be assigned or cast to any reference type (§5.2, §5.3, §5.5).
In practice, the programmer can ignore the null type and just pretend that null is merely a special literal that can be of any reference type.

这里说null是一种特殊的类型,这个类型表示它的字面意思null,并且没有名称。因为null类型没有名称,所以我们不能声明一个null类型的对象,也不能把别的类型转换成null类型。
null类型唯一可能的值就是null引用,虽然有点拗口,理解成,null表示的就是他的字面意思就好了。
null引用可以赋给任何引用类型,也可以转换成任意引用类型。
程序员就把null看成字面意思就好了。
所以null究竟是什么类型JLS已经说清楚了,针对上面的介绍我写了一个类验证了下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class NullTest implements Algorithm {
@Override
public void execut() {
((Anything)null).printStatic();
Utils.printString("" + ((Anything)null));
((Anything)null).print();
}

public static class Anything {
public static void printStatic(){
Utils.printString("in static");

}

public void print(){
Utils.printString("in not static");
}
}
}

上面程序的运行结果是

1
2
3
4
5
6
7
8
9
10
in static
null
Exception in thread "main" java.lang.NullPointerException
at com.zgq.java.NullTest.execut(NullTest.java:14)
at com.zgq.Main.main(Main.java:15)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)

可见静态方法是可以输出的,因为是类的方法,而null转对象结果还是null。用null去调实例方法自然要报空指针。
说到这第二个问题就很明显了,null显然不是某个具体的示例。
那么,null是什么呢?这个问题很难直接回答,或者说很难按照正常的逻辑去回答,我们在学java的时候,会有一种感觉,一切都是对象,一切且有源头,任何对象任何类我都能从源码去一窥一二,可是null不行,我们无法直接拿出证据去精确的描述它,但是我们可以更实际一些的从他的作用来分析null是什么:

  1. java中,null == null是永远返回true的
  2. 对于非空引用t,t.equals(null)返回false
  3. null是一切引用类型的初始值
  4. null用来表示不存在的对象
  5. null表示未知的值
  6. null表示某种情况的终点
  7. HashMap中的key和value都可以是null,但这就带来一个问题:假设一个map,key和value都是Integer,map中包含一个键值对4-null,那么map.get(4)返回值是null,如果map中没有键值4,那么返回值还是null,这就产生语义冲突。当然java的HashTable是不存在这个问题的,这里不细说。

null在内存里是什么样的?
我们从其他方面侧面看下

1
String s = null;

上面这行代码做的唯一一件事就是给s分配一个引用的内存空间,可能是32位,也可能64位,和硬件及java版本有关,这里不会给null分配任何的内存空间,因为null不是一个具体的对象。它像一个占位符一样表示这个引用还没有指向具体的对象。可能这个占位符就是个0,谁知道呢?

以下为个人想法,首先null这种特殊的引用类型在内存中肯定是存在的(废话),但是怎么存在的呢?我们看下equals方法

1
2
3
 public boolean equals(Object obj) {
return (this == obj);
}

我们知道t.equals(null)返回false,可见对null的equals判断是在运算符中做的,线索断了,暂时未找到切入点。