Java引用类型是class、interface、array、enum、record的统称,变量存储堆中对象地址而非实际数据;null为其默认值;含强、软、弱、虚四种引用强度;==比较地址,equals()默认也比地址,需重写才比内容;Java只有值传递,非引用传递。

Java 中的“引用类型”不是某种特殊的数据类型,而是对 class、interface、array、enum、record 这五类类型的统称——它们的变量存储的不是实际数据,而是堆内存中对象的地址。
引用类型变量到底存的是什么
声明一个 String s = "hello";,变量 s 本身在栈上,值是一个 32 位或 64 位的内存地址(取决于 JVM 是否开启压缩指针),指向堆中真正的 String 对象。这个地址本身不是对象,也不能直接算术运算,只用于定位。
常见误解是“引用就是指针”,但 Java 引用被刻意设计为不可见、不可操作的逻辑地址,JVM 可以在 GC 时移动对象并自动更新所有引用,开发者完全感知不到。
-
null是引用类型的默认初始值,表示“不指向任何对象” - 基本类型(如
int、boolean)变量直接存值,不存在null - 数组是引用类型,哪怕元素是基本类型:
int[] arr中arr是引用,arr[0]才是值
四种引用强度:强、软、弱、虚
JDK 提供了 java.lang.ref 包下的四类包装类,用于控制对象被 GC 回收的时机,和普通变量声明无关,必须显式使用。
立即学习“Java免费学习笔记(深入)”;
它们的区别不在语法,而在 JVM 的回收策略:
-
StrongReference:日常所有赋值都是强引用,只要可达就不会被回收 -
SoftReference:内存不足时才回收,适合做缓存(如SoftReference) -
WeakReference:GC 时立刻回收,常用于避免内存泄漏(如WeakHashMap的 key) -
PhantomReference:无法通过它访问对象,仅用于在对象被回收后收到通知,必须配合ReferenceQueue
示例:
WeakReferencewr = new WeakReference<>(new String("temp")); System.gc(); // wr.get() 很可能返回 null
== 和 equals() 判等差异的本质原因
因为引用类型变量存的是地址,所以 == 比较的是两个变量是否指向堆中**同一个对象**;而 equals() 默认行为也是 ==(继承自 Object),只有重写后才可能比较内容。
-
String a = "abc"; String b = "abc";→a == b为true(字符串常量池优化) -
String a = new String("abc"); String b = new String("abc");→a == b为false,但a.equals(b)为true - 自定义类若没重写
equals(),两个不同实例即使字段全同,equals()也返回false
容易被忽略的引用陷阱
很多内存问题和逻辑 bug 都源于对引用传递机制的误判。
- Java 中**没有引用传递,只有值传递**:方法参数接收的是引用的副本,修改该副本指向新对象不影响原引用;但通过该副本修改对象内部状态(如
list.add()),会影响原对象 - 循环引用不会阻止 GC:JVM 使用可达性分析,不是引用计数,
A.ref = B; B.ref = A;且无外部强引用时,两者都会被回收 - 局部变量引用的对象,在方法结束后不一定立即可回收——只要还有其他强引用(如静态集合、线程局部变量),就会一直存活
最常踩的坑是把大对象塞进 static 集合却忘了清理,导致内存泄漏,而堆 dump 里看到的“引用链”往往很长,源头却只是某次忘记 remove() 的操作。










