Java——详解形参实参方法的重载
文章目录
- 一、实参和形参的关系
- 二、方法的重载
一、实参和形参的关系
1.首先我们需要知道什么是形参什么是实参。
例如:
public int add(int a,int b)
{
return a+b;
}
//这里的a和b就是形参
//add(2,3)2和3就是实参,在调用时传给形参a和b
注意:在Java中,实参的值永远都是拷贝到形参中,形参和实参本质是两个实体
2.交换两个整型变量
public class TestMethod {public static void main(String[] args) {int a = 10;int b = 20;swap(a, b);System.out.println("main: a = " + a + " b = " + b);}public static void swap(int x, int y) {int tmp = x;x = y;y = tmp;System.out.println("swap: x = " + x + " y = " + y);}
}// 运行结果
//swap: x = 20 y = 10
//main: a = 10 b = 20
为什么会出现这样的情况呢?明明swap方法里面x和y已经交换了值,主方法里面的a和b却没有交换。
原因解释:
1.栈帧隔离:每次调用 invokestatic / invokevirtual 指令,JVM 都会在线程栈上为被调方法创建一个新的 Stack Frame。
局部变量表位于该帧内部,仅当前帧可见。
调用者的局部变量表与被调者的局部变量表物理隔离。
2.实参 → 形参 的复制过程:把调用者栈帧操作数栈顶上的 实参值复制一份将这份副本按顺序存入被调方法栈帧的局部变量,进入被调方法的字节码执行后,所有对形参的读写都只在当前帧的这些槽位进行,与调用者帧毫无关联。
Java 的所有参数传递都是“值传递”——实参把值复制一份给形参;方法里对形参重新赋值,只是改了这份副本,所以实参永远不变。
3.解决办法:传引用类型参数 (例如数组来解决这个问题)
public class TestMethod {public static void main(String[] args) {int[] arr = {10, 20};swap(arr);System.out.println("arr[0] = " + arr[0] + " arr[1] = " + arr[1]);}public static void swap(int[] arr) {int tmp = arr[0];arr[0] = arr[1];arr[1] = tmp;}
}// 运行结果
//arr[0] = 20 arr[1] = 10
接下来聊聊为什么能成功的原因
在此之前,我们先了解一下JVM的内存分布
程序计数器 (PC Register): 只是一个很小的空间, 保存下一条执行的指令的地址
虚拟机栈(JVM Stack): 与方法调用相关的一些信息,每个方法在执行时,都会先创建一个栈帧,栈帧中包含有:局部变量表、操作数栈、动态链接、返回地址以及其他的一些信息,保存的都是与方法执行时相关的一些信息。 比如:局部变量。当方法运行结束后,栈帧就被销毁了,即栈帧中保存的数据也被销毁了。
本地方法栈(Native Method Stack): 本地方法栈与虚拟机栈的作用类似. 只不过保存的内容是Native方法的局部变量. 在有些版本的 JVM 实现中(例如HotSpot), 本地方法栈和虚拟机栈是一起的
堆(Heap): JVM所管理的最大内存区域. 使用 new 创建的对象都是在堆上保存 (例如 new int[]{1, 2, 3} ),堆是随着程序开始运行时而创建,随着程序的退出而销毁,堆中的数据只要还有在使用,就不会被销毁。
方法区(Method Area): 用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据. 方法编译出的的字节码就是保存在这个区域
基本数据类型创建的变量,称为基本变量,该变量空间中直接存放的是其所对应的值;
引用数据类型创建的变量,一般称为对象的引用,其空间中存储的是对象所在空间的地址。就像这样
引用变量并不直接存储对象本身,可以简单理解成存储的是对象在堆中空间的起始地址。通过该地址,引用变量便可以去操作对象。
引用数据类型可以正确交换的原因:
交换的是堆对象的“内部状态”,而非形参变量本身。
方法里拿到的是对象引用的副本,但两个副本指向同一块堆内存。
通过该引用去修改对象的字段(或数组元素),所有持有同一引用的代码都会看到变化。因此,交换的是对象内部的数据,而不是交换形参变量里的引用值。
二、方法的重载
什么是方法重载:指在同一个类中,方法名相同,但参数列表不同(个数、类型或顺序不同)的一组方法。编译器根据调用时传入的实参列表,静态绑定(编译期)到最匹配的那个重载版本。
例如:
public class TestMethod {public static void main(String[] args) {add(1, 2); // 调用add(int, int)add(1.5, 2.5); // 调用add(double, double)add(1.5, 2.5, 3.5); // 调用add(double, double, double)}public static int add(int x, int y) {return x + y;}public static double add(double x, double y) {return x + y;}public static double add(double x, double y, double z) {return x + y + z;}
}
1. 方法名必须相同
2. 参数列表必须不同(参数的个数不同、参数的类型不同、类型的次序必须不同)
3. 与返回值类型是否相同无关
注意:两个方法如果仅仅只是因为返回值类型不同,是不能构成重载的
##三、 方法签名
为什么可以定义相同方法名的方法呢,要如何区分这些方法呢?接下来便让我们的主角登场。
方法签名即:经过编译器编译修改过之后方法最终的名字。具体方式:方法全路径名+参数列表+返回值类型,构成方法完整的名字。
查看方法签名的方法:
- 先对工程进行编译生成.class字节码文件
- 在控制台中进入到要查看的.class所在的目录
- 输入:javap -v 字节码文件名字即可
可以看到即使方法名相同,但签名不同。