在 Java 中,数据类型分为两大类:基本类型(值类型) 和引用类型。它们在内存分配、存储方式、传递机制等方面有着本质区别。
一、基本类型(Primitive Types)
也称为值类型,Java 中共有 8 种基本类型:
| 类型 | 大小 | 默认值 | 包装类 |
|---|---|---|---|
byte | 1 字节 | 0 | Byte |
short | 2 字节 | 0 | Short |
int | 4 字节 | 0 | Integer |
long | 8 字节 | 0L | Long |
float | 4 字节 | 0.0f | Float |
double | 8 字节 | 0.0d | Double |
char | 2 字节 | '\u0000' | Character |
boolean | 不确定(通常 1 字节) | false | Boolean |
特点:
- 变量直接存储值本身。
- 声明在方法中的局部变量,其值存放在栈内存中。
- 实例变量(对象中的字段)的值存放在堆内存中(随对象一起分配)。
- 赋值操作会复制整个值,互不影响。
- 默认值明确(如
int默认 0,boolean默认false)。
二、引用类型(Reference Types)
包括类、接口、数组、枚举、注解等所有非基本类型的变量。
特点:
- 变量存储的是对象的引用(内存地址) ,而不是对象本身。
- 引用类型变量本身(在栈中)指向堆内存中实际存储对象的位置。
- 赋值操作复制的是引用,即两个变量指向同一个对象,修改其中一个会影响另一个。
- 默认值为
null。 - 数组也是引用类型,即使元素是基本类型(如
int[])。
示例:
java
String s; // s 是引用类型变量,默认 null
s = new String("hello"); // 栈中的 s 存储堆中对象的地址
三、关键区别对比
| 对比项 | 基本类型 | 引用类型 |
|---|---|---|
| 存储内容 | 值本身 | 对象的地址(引用) |
| 内存分配 | 通常栈中(局部变量)或堆中(实例字段) | 变量在栈,对象在堆 |
| 默认值 | 有具体默认值(如 0, false) | null |
| 传递方式 | 值传递(传递副本) | 值传递(传递引用的副本,但指向同一对象) |
| 大小 | 固定大小(已知字节数) | 变量大小固定(引用地址大小),对象大小可变 |
| 可空性 | 不能为 null(除非使用包装类) | 可以为 null |
| 内存回收 | 随栈帧弹出或所在对象回收而释放 | 对象由 GC 回收,当没有引用指向时 |
四、传递机制详解
Java 中所有参数传递都是值传递(pass-by-value)。但这里的“值”对于引用类型来说,是引用的副本。
-
基本类型:传递的是值的副本,方法内修改不影响外部变量。
java
void change(int x) { x = 10; } int a = 5; change(a); System.out.println(a); // 输出 5 -
引用类型:传递的是引用的副本,两个引用指向同一对象,方法内修改对象状态会影响外部,但重新赋值引用不会影响外部。
java
void change(StringBuilder sb) { sb.append(" World"); // 修改对象内容,外部可见 sb = new StringBuilder("new"); // 重新赋值引用,外部不可见 } StringBuilder sb = new StringBuilder("Hello"); change(sb); System.out.println(sb); // 输出 "Hello World"
五、包装类与自动装箱/拆箱
为了将基本类型当作对象使用,Java 为每个基本类型提供了对应的包装类(如 Integer、Double)。Java 1.5 引入了自动装箱和自动拆箱,使基本类型与包装类可以自动转换。
java
Integer i = 10; // 自动装箱:int -> Integer
int j = i; // 自动拆箱:Integer -> int
注意:包装类是引用类型,但 JVM 对部分常用值(如 Integer 的 -128~127)进行了缓存,直接使用 == 比较时可能产生意外结果,推荐用 equals。
六、总结
- 基本类型:直接存值,效率高,用于简单数据。
- 引用类型:存地址,支持多态、继承,用于复杂对象。
- 理解两种类型的内存模型和传递方式,是写出正确 Java 程序的基础,尤其在多线程、集合操作、方法设计时至关重要。