引用类型的默认值是null,根本原因在于语义与内存模型的严格分离:null表示“无对象”,是专用空标记;而0表示“有数值”,属基本类型零值;jvm对成员变量执行类型相关零值初始化,引用类型统一设为null,非数字0的复用。

引用类型的默认值是 null,不是 0,根本原因在于语义与内存模型的严格分离。
语义上:null 表示“无对象”,0 表示“有数值”
引用类型变量存的不是数据本身,而是对象在堆内存中的地址。如果默认设为 0,容易被误解为“指向地址 0 的对象”,但地址 0 通常不可访问(会触发段错误或空指针异常),而且现实中并不存在一个合法对象恰好位于地址 0。而 null 是一个专用标记,明确表达“当前没指向任何有效对象”,不参与计算,也不代表任何实际内存位置。
- String s; → s 是 null,说明还没分配字符串对象
- int i; → i 是 0,说明它是一个确定的整数值
- 把 null 当作 0 使用会编译失败或运行时报错,语言层面就阻止了这种混淆
内存与初始化机制决定它不能是 0
JVM(或 .NET 运行时)对成员变量执行“零值初始化”:所有引用字段统一设为 null,所有数值字段设为对应零值(0、0.0、false)。这个“零值”是类型相关的,不是统一填 0 字节——比如 boolean 默认 false,不是 0;char 默认 \u0000,也不是整数 0。所以 null 是引用类型的“零值”,不是数字 0 的复用。
- class A { String name; } → new A().name 一定是 null,由 JVM 在对象创建时写入
- 局部变量 String s; 不允许直接使用,必须显式赋值,否则编译报错——这进一步说明 null 是受控的、有意的设计,不是随意填充的占位符
安全与一致性需要一个专属空值
用 0 代替 null 会破坏类型系统:
- 如果 int 和 String 都默认为 0,那类型检查就失去意义
- 数组、List、自定义类等引用类型无法共用一个数值来表示“空”
- null 可以被统一识别、统一判空(如 if (obj == null))、统一用于释放资源或终止链式调用
- 现代语言如 Kotlin、Rust、TypeScript 甚至引入非空类型或可空标注,正是基于 null 的明确语义做空安全增强
例外情况不改变本质
某些场景下你可能看到“类似 0 的表现”,但那不是默认值本身:
- new int[5] 的元素是 0 → 这是数组**元素**的默认值,不是数组变量本身的值(数组变量仍是 null,直到 new 执行后才指向实例)
- C/C++ 中 NULL 常定义为 ((void*)0) → 这是底层实现兼容性技巧,上层语言(如 Java)已抽象掉该细节,null 不再等价于数值 0
- 数据库中 NULL 和 0 是不同概念,程序映射时也需区分,正说明它们语义不可互换











