黑马Java基础笔记-4
方法
什么是方法
方法是程序中最小的执行单元。
形参和实参
调用
直接调用
getSum(10,20,30);
赋值调用
int sum = getSum(10,20,30);
输出调用
System.out.println(getSum(10,20,30));
方法的重载
在同一个类中,定义了多个同名的方法,这些同名的方法具有同种的功能。
每个方法具有不同的参数类型或参数个数 (返回类型可以不同) ,这些同名的方法,就构成了重载关系。
方法调用的基本内存原理
调用方法进栈。
方法调用完移出栈(里面的变量也会被销毁)。
方法调用进栈
当一个方法被调用时,JVM 会为该方法分配一个新的栈帧(Stack Frame),压入到当前线程的调用栈顶。这个栈帧里保存了该方法的局部变量表、操作数栈、方法返回地址等信息。
方法调用完出栈
当方法执行结束(正常返回或抛出未捕获的异常)后,对应的栈帧会从栈顶弹出,方法中的局部变量随之被销毁,无法再被访问。
局部变量与栈帧的生命周期
- 在方法栈帧内声明的局部变量只在该方法执行期间有效。
- 方法执行结束,局部变量就会随栈帧一起销毁。
- 如果局部变量是“引用类型”(如
String
、自定义对象等),则栈中保存的是对堆中对象的引用;当栈帧销毁后,这些引用也会随之消失,但堆中的对象是否被销毁取决于是否还有其他引用指向它(如果没有其他引用了,就等待垃圾回收机制进行回收)。
扩展
在 Java 虚拟机(JVM)层面,当一个方法被调用时,JVM 会为该方法分配一个栈帧(Stack Frame),其中主要包含三个核心部分:
- 局部变量表(Local Variable Table)
- 操作数栈(Operand Stack)
- 方法返回地址(Return Address)
public class Example {public static void main(String[] args) {int a = 5; // (1)int b = 6; // (2)int c = add(a, b); // (3)System.out.println(c);}public static int add(int x, int y) {return x + y; // (4)}
}
方法 main
的执行过程
- main 方法被调用
当 JVM 启动程序时,首先调用main(String[] args)
方法。此时会创建一个对应的栈帧并压入当前线程的调用栈顶。 - 局部变量表(Local Variable Table)
- 在
main
方法的栈帧中,局部变量表会存放:args
(形参,通常位于槽位 0)a
(槽位 1)b
(槽位 2)c
(槽位 3)
- 比如执行
int a = 5;
时,就把5
存入了局部变量表的某个槽位(Slot)里。
- 在
- 操作数栈(Operand Stack)
- 当执行诸如
int c = add(a, b);
这类语句时,JVM 指令会先把a
、b
的值压入操作数栈,然后调用add
方法。 - 在调用
add
方法时,会把操作数栈顶的值(即a
和b
)传递给被调用方法的形参(x
和y
)。
- 当执行诸如
- 方法返回地址(Return Address)
- 在
main
方法调用add
时,JVM 需要知道当add
方法执行完毕后,该返回到main
的哪条指令继续执行。这个信息(返回地址)会存放在当前栈帧中,等到add
方法结束后,就会使用这个返回地址跳回main
。
- 在
- 调用结束,返回结果
- 当
add
方法执行完return x + y;
后,会把结果(x + y
)压回调用者的操作数栈中,并把add
方法的栈帧弹出。 - 回到
main
方法后,操作数栈顶部的值就是x + y
的结果,此时存入c
这个局部变量槽位,继续执行后续的System.out.println(c);
。
- 当
方法 add
的执行过程
- add 方法被调用
- 在执行
int c = add(a, b);
时,JVM 会给add
创建一个新的栈帧。
- 在执行
- 局部变量表
add
方法的形参x
、y
分别保存在这个新栈帧的局部变量表中,一般是槽位 0 和槽位 1。
- 操作数栈
- 执行
return x + y;
时,会把x
和y
的值压入操作数栈,然后执行iadd
指令完成加法运算。
- 执行
- 方法返回地址
- 当
add
执行结束时,需要返回到调用它的地方(即main
方法的某条指令处)。所以add
方法的栈帧中也存有返回地址信息,执行完后会弹出该栈帧,并将结果返回给main
。
- 当
总结
- 局部变量表(Local Variable Table)
用来存放方法的参数和局部变量,包括基本数据类型以及对象引用(Reference)等。可以把它想象成一个小型数组或表,每个方法调用都会有自己独立的一份。 - 操作数栈(Operand Stack)
用来执行各种字节码指令时的中间操作数,比如做加法、乘法、对象引用的操作等。操作数栈是“后进先出”的结构,用来暂时存放操作过程中需要使用的值。 - 方法返回地址(Return Address)
用来记录当前方法调用完毕后,需要跳转回上一个调用方法(调用者)的哪个字节码指令处继续执行。这个地址在方法正常返回或异常返回时都会被用来恢复现场。
方法的值传递
//调用后还是100.
Java方法参数传递均为值传递:
- 基本类型传递数据副本,形参修改不影响实参。
- 对象类型传递地址副本,修改对象属性会影响原对象,但形参重新赋值不会影响实参。(如
obj = new Object()
)不会影响原始对象引用。