Java 中 String 是一个引用类型,它的默认值是 null。

下面我们从几个层面来深入理解这个问题。
为什么默认值是 null?
在 Java 中,变量分为两大类:基本类型 和引用类型。
| 类型 | 分类 | 默认值 | 示例 |
|---|---|---|---|
int, double, boolean 等 |
基本类型 | 0, 0, false |
int num; |
String, Array, Object 等 |
引用类型 | null |
String str; |
核心原因:
- 内存分配方式不同:
- 基本类型:变量直接存储值本身,当声明一个基本类型变量但没有初始化时,JVM 会为它分配一块内存,并设置一个默认的“零值”(
0对于数值型,false对于布尔型,'\u0000'对于字符型),这个变量是“可用”的。 - 引用类型:变量存储的是对象在内存中的地址(引用),而不是对象本身,当声明一个引用类型变量但没有初始化时,这个引用变量没有被指向任何有效的对象,为了表示“什么都没指向”,Java 规定了它的默认值为
null。
- 基本类型:变量直接存储值本身,当声明一个基本类型变量但没有初始化时,JVM 会为它分配一块内存,并设置一个默认的“零值”(
你可以把引用类型变量想象成一个遥控器。null 意味着这个遥控器还没有被绑定到任何电视机上,你不能通过一个未绑定的遥控器去操作电视(比如按“开机”键),否则就会出错。

在不同场景下的默认值表现
a. 成员变量(实例变量或类变量)
当一个类的成员变量没有显式初始化时,它们会自动获得默认值。
public class MyClass {
// 成员变量,没有显式初始化
private String myString;
private int myInt;
public void printDefaults() {
// 输出成员变量的默认值
System.out.println("myString 的默认值: " + this.myString); // 输出: null
System.out.println("myInt 的默认值: " + this.myInt); // 输出: 0
}
}
public class Main {
public static void main(String[] args) {
MyClass obj = new MyClass();
obj.printDefaults();
}
}
输出:
myString 的默认值: null
myInt 的默认值: 0
b. 局部变量
这是最容易出错的地方。局部变量(方法内声明的变量)没有默认值,Java 编译器强制要求在使用前必须显式初始化它们,否则编译会报错。
错误示例:

public class MyMethod {
public void myMethod() {
String localString; // 声明了一个局部变量,但没有初始化
// 下面这行代码会导致编译错误!
// System.out.println(localString);
// 错误信息: Error: variable localString might not have been initialized
}
}
正确做法:
public class MyMethod {
public void myMethod() {
String localString = null; // 显式初始化为 null
// 或者 String localString = "Hello"; // 显式初始化为一个字符串字面量
System.out.println(localString); // 现在可以正常输出: null
}
}
为什么使用 null 会导致 NullPointerException?
这是 Java 开发中最常见的运行时异常,当你尝试对一个 null 引用调用方法或访问其成员时,就会抛出此异常。
public class NullPointerExample {
public static void main(String[] args) {
String str = null;
// 尝试调用一个方法
// str.length(); // 这行代码会抛出 NullPointerException
// 尝试访问一个字符
// str.charAt(0); // 这行代码也会抛出 NullPointerException
}
}
原因分析:
str变量存储的值是null,表示它没有指向任何String对象。- 当你执行
str.length()时,JVM 会尝试去str存储的地址(即null)处寻找length方法。 - 但
null不是一个有效的内存地址,JVM 找不到任何东西,程序崩溃并抛出NullPointerException。
最佳实践:如何避免 NullPointerException?
处理 null 是 Java 编程的重要部分,以下是一些常用策略:
a. 显式检查
这是最直接的方法,在使用前检查变量是否为 null。
String str = maybeGetString(); // 这个方法可能返回 null
if (str != null) {
int length = str.length();
System.out.println("字符串长度是: " + length);
} else {
System.out.println("字符串是 null,无法获取长度。");
}
b. 使用 java.util.Objects.requireNonNull
当你希望某个参数不能为 null 时,可以在方法入口处进行检查,如果为 null,它会立即抛出 NullPointerException,使问题尽早暴露。
public void process(String input) {
// 确保 input 不为 null,否则快速失败
Objects.requireNonNull(input, "输入参数不能为 null");
// ... 安全地使用 input
System.out.println(input.toUpperCase());
}
c. 使用 Java 8+ 的 Optional 类
Optional 是一个容器对象,它可以包含或者不包含非 null 值,它旨在减少 null 的使用,让代码意图更明确。
import java.util.Optional;
public class OptionalExample {
public static void main(String[] args) {
String nullableString = null;
// String nullableString = "Hello";
// 将可能为 null 的字符串包装成 Optional
Optional<String> optionalStr = Optional.ofNullable(nullableString);
// 如果存在值则执行操作,否则提供默认值
String result = optionalStr.orElse("默认值"); // nullableString 为 null, result "默认值"
// 如果存在值则执行操作,否则什么也不做
optionalStr.ifPresent(s -> System.out.println("字符串是: " + s));
System.out.println("最终结果是: " + result);
}
}
d. 使用现代 Java 的空值安全操作符
Java 8 引入了 Optional,Java 14 引入了更方便的空值安全操作符。
- (安全调用操作符): 如果对象为
null,则整个表达式返回null,而不会抛出异常。 - (Elvis 操作符): 如果左侧表达式为
null,则返回右侧的默认值。
String str = null; // 安全调用,str 为 null,str.length() 不会执行,整个表达式结果为 null Integer length = str?.length(); // 在 Java 中不直接支持,但在 Kotlin/Scala 等语言中很常见 // Java 中的等价写法(使用 Optional) Optional.ofNullable(str).map(String::length).orElse(null); // Elvis 操作符的等价写法 String result = (str != null) ? str : "默认值";
注意:Java 语言本身目前(Java 21)没有原生支持 和 操作符,这些特性主要存在于 Kotlin、Groovy 等运行在 JVM 上的语言中,在 Java 中,我们通常用
Optional或三元运算符来实现类似逻辑。
| 场景 | 默认值 | 是否允许直接使用 | 原因 |
|---|---|---|---|
| 成员变量 | null |
不允许 (会抛出 NullPointerException) |
变量存在,但没有指向任何对象。 |
| 局部变量 | 无默认值 | 不允许 (编译错误) | 强制开发者显式初始化,提高代码健壮性。 |
记住这个核心原则:String 是引用类型,默认值为 null,任何试图通过 null 引用访问对象的行为都会导致 NullPointerException,养成良好的编码习惯,如显式检查、使用 Optional 或工具类,可以有效避免这类错误。
