1. 类型转换的本质:从内存视角理解Java多态
Java中的类型转换是初学者最容易困惑的概念之一。要真正理解它,我们需要从内存分配的角度切入。让我们先看这段典型代码:
java复制Animal a = new Cat();
Cat c = (Cat) a;
1.1 堆与栈的基础分工
在JVM中,内存主要分为两大区域:
-
栈(Stack):方法调用的临时工作区
- 存储局部变量(包括基本类型和引用变量)
- 每个线程有独立的栈空间
- 自动分配和释放,效率高但空间有限
-
堆(Heap):对象存储的主仓库
- 存储所有通过new创建的对象实例
- 被所有线程共享
- 需要GC管理,空间大但访问稍慢
提示:可以把栈想象成餐厅的临时餐桌,用完就收;堆则是后厨的储物间,食材(对象)长期存放直到被清理。
1.2 引用变量的存储机制
当执行Animal a = new Cat()时,内存中发生了这些变化:
-
右侧
new Cat():- 在堆中分配内存创建Cat对象
- 对象包含Animal父类的属性和Cat特有的属性
-
左侧
Animal a:- 在栈中创建引用变量a
- a的类型声明为Animal(编译期类型)
-
赋值操作
=:- 将Cat对象的内存地址存入变量a
- 此时a的运行时类型是Cat
内存布局示意:
code复制栈 堆
+------+ +---------------+
| a | --------> | Cat实例 |
| (Animal引用)| | - Animal部分 |
+------+ | - Cat特有部分 |
+---------------+
1.3 向上转型的合理性
为什么父类引用可以指向子类对象?这涉及几个关键点:
- 继承关系保证:子类必定包含父类的所有属性和方法
- 类型安全:通过父类引用只能访问父类声明的成员
- 多态基础:运行时才能确定具体子类类型
就像我们可以说"猫是动物",
