首页 > 基础资料 博客日记
Java中的实参和形参
2025-01-17 08:00:08基础资料围观22次
一、背景
为了回顾Java的一些基础但不常用的知识,于是打算出一个合集,方便迅速查找(主要还是不想翻课本,并且把概念和脉络厘清一些)
- 对于初学者而言,对这方面的理解可能是:在函数内修改形参的值,会导致实参发生改变吗?
- 对于有一定编程基础的同学,可能会感到疑问:Java是值传递还是引用传递?
- 对于编写过程序,但对概念不是特别清晰的小伙伴,可能会得出这么一个结论:在函数内修改形参的值,有时会导致实参的值发生改变,有时不会。Java有时是值传递,有时是引用传递。
(*´・д・)? (*´・д・)? (*´・д・)?
二、结论
先说结论:
在函数内修改形参的值,不会导致实参的值发生改变,但可能会导致实参指向的对象发生改变,所以看起来像是实参发生了改变
Java是值传递
其实这些结论的意思并不矛盾,但对于初学者来说,可能不太能够理解这些概念。因此我会从头分析这些概念的含义,并解释出现“矛盾”的原因。
如果对这些概念比较清楚的小伙伴,可以自行跳过啦!
三、解释
1.“实参”、“形参”、“值传递”、“引用传递”
提前声明:概念的解释不一定很规范,只是个人的理解
a.实参
实参是调用方法时传入的具体值或对象。
它们实际在方法调用时传递给形参。
add(3, 5);//这里的3,5为实参
b.形参
形参是定义方法时,在方法声明中使用的参数。
它们是方法的“占位符”,表示将要传入方法的值或对象。形参在方法体内部作为局部变量使用。
public int add(int a, int b) {//这里的a,b为形参
int sum = a + b;
return sum;
}
c.值传递
在调用函数时,将实际参数复制一份传递到函数中
顾名思义,就是将参数的值传递给形参。既然是值,那么相当于和原来的实参不再有关系。
d.引用传递
在调用函数时,将实际参数的地址直接传递到函数中
顾名思义,就是将参数换个名字(别名)传递给形参。既然是别名,那么实际上还是同一个变量。
综上所述,“Java是值传递”就代表“在函数内修改形参的值,不会导致实参发生改变”。因为传递的只是拷贝。
这时候可能会有人有疑惑:嘶~不对呀,那我传递数组时,在函数里面修改数组的值,实参也发生改变了呀,这是怎么回事?
没错,这就是看似“矛盾”的地方,我们接着往下看
2.Java 的数据类型
a.Java 的两大数据类型:
-
内置数据类型:Java语言提供了八种基本类型。六种数字类型(四个整数型,两个浮点型),一种字符类型,还有一种布尔型。byte、short、int、long、float、double、boolean、char。
-
引用数据类型:在Java中,引用类型的变量非常类似于C/C++的指针。引用类型指向一个对象,指向对象的变量是引用变量。
我相信对于没学过的其它编程语言的初学者来时,引用数据类型的定义十分晦涩难懂,在不了解“指针”这个概念的前提下,很难理解“指向”的具体含义。这是人之常情,也无可厚非。学习没有必要追求每一个细节,很多概念的理解在了解完后面的知识后便会豁然开朗。我当初对于这个概念就直接跳过,并且这个知识点出现的频率很低。第一次遇到这个问题时,自己琢磨出“new出来的变量作为实参传递给函数时,在函数里面修改会导致实参改变”这个一个粗糙的结论。
在这里简单介绍一下“指针”的概念(解释不规范,只是为了本文需要)
- 如果我们把操作“int x = 1;”分为声明变量和赋值
- “int x;”为声明变量,数据类型为int,对应下表的名称; “x=1;”为赋值,对应下表的值
- 对于内置数据类型而言,上述操作只需要一个表即可
- 对于引用数据类型而言,定义变量的操作则需要两个表。
“A a;”为声明变量创建一个表,数据类型为A,名称为a(与上面相同)。
“a = new A();”可以分成两步。
第一步是创建对象,即“new A();”操作,这是创建一个值的过程(实例化),需要创建一个新的表。
第二步才是给a赋值(关键!),a的值为第一步中新创建的对象的地址。也就是说,a的值实际上是一个地址,这个地址指向一个对象。
至于为什么会有引用数据类型,为什么不能像内置数据类型一样直接把新对象的值放在a的值里,就不是本文需要探讨的内容,有兴趣的小伙伴可以自己探索一下。
b.String类型
值得注意的是,根据上述的定义我们知道String类型不属于内置数据类型,而属于引用数据类型,但String类型表现的像内置数据类型。
在 Java 中,
String
是一个引用数据类型,而不是基本数据类型。尽管
String
在许多方面表现得像基本数据类型(例如,它具有类似于基本数据类型的常量池、不可变性等特点),但它仍然是一个类,属于引用数据类型。你可以把String
看作是一个特殊的类,Java 对它做了一些优化,使得它在使用时像基本数据类型一样高效和方便。基本数据类型(如
int
、char
、boolean
等)是直接存储在栈中的值,而String
对象是存储在堆中的。
然而在大多数情况下,都可以把String类型当作是基本数据类型(一直以来我都是这么干的,哈哈哈(→ܫ←))。但还是需要稍微厘清一下这个概念。
3.矛盾分析
有了上述理论知识,那么理解“矛盾”的根本所在就很清晰了
Java仍然是值传递
也就是说,在a作实参时,传递给形参:a的值的拷贝。
因此不变的是a的值(也就是对象的地址),改变的是对象。
很好理解,对于局部变量形参来说,形参的值是a的值拷贝,所以形参和实参指向的是同一个对象。借助形参改变了对象,所以在函数外部看起来就像是:函数改变了实参。
实际上实参的值没改变,只是实参的值所指向的对象改变了,不知道这样的说法能不能理解。
4.代码演示
a.实验代码:
- 测试内置数据类型,在函数内修改形参的值
- 测试引用数据类型,在函数内修改形参的值
- 测试引用数据类型,在函数内修改形参指向的对象
- 测试String类
public class Main {
public static void main(String []args) {
System.out.println("****************************************************");
int x=1;//内置数据类型
System.out.println("原来的x:"+x);//输出x的值
functionOnBasic(x);//在函数中改变形参的值
System.out.println("调用函数后x:"+x);//输出x的值
System.out.println("****************************************************");
classA a=new classA();//引用数据类型
System.out.print("原始:a:"+a);//输出a的值,地址,指向其它对象
System.out.println(";属性:"+a.attribute);//借助a的值来访问对象的属性
functionOnClassOne(a);//在函数中改变形参的值
System.out.print("调用函数一后:a:"+a);//输出a的值,地址,指向其它对象,未变
System.out.println(";属性:"+a.attribute);//借助a的值来访问对象的属性
System.out.println("****************************************************");
functionOnClassTwo(a);//在函数中改变形参指向的对象
System.out.print("调用函数二后:a:"+a);//输出a的值,地址,指向其它对象
System.out.println(";属性:"+a.attribute);//借助a的值来访问对象的属性,改变
System.out.println("****************************************************");
String str=new String("欢迎来看我的blog");
System.out.println("原来的str:"+str);
functionOnString(str);
System.out.println("调用函数后str:"+str);
}
public static void functionOnBasic(int number)
{
number = -1;
}
public static void functionOnClassOne(classA a)
{
a=new classA(100);
System.out.println("改变形参的值,此时形参的值:"+a+";属性为:"+a.attribute);
}
public static void functionOnClassTwo(classA a)
{
a.attribute=-1;
}
public static void functionOnString(String s)
{
s="我在函数中被改变了呀";
}
}
class classA{
public int attribute;
public classA(){
attribute=10;
}
public classA(int newNumber){
attribute=newNumber;
}
}
b.实验结果:
****************************************************
原来的x:1
调用函数后x:1
****************************************************
原始:a:classA@b4c966a;属性:10
改变形参的值,此时形参的值:classA@2f4d3709;属性为:100
调用函数一后:a:classA@b4c966a;属性:10
****************************************************
调用函数二后:a:classA@b4c966a;属性:-1
****************************************************
原来的str:欢迎来看我的blog
调用函数后str:欢迎来看我的blog
Process finished with exit code 0
c.结果分析
- 测试内置数据类型,在函数内修改形参的值:
形参的改变不影响实参的值,实参的值未变:1
- 测试引用数据类型,在函数内修改形参的值:
形参的改变不影响实参的值,实参的值未变:classA@b4c966a,对象的属性未变:10
- 测试引用数据类型,在函数内修改形参指向的对象
形参的改变不影响实参的值,实参的值未变:classA@b4c966a,对象的属性改变:-1
- 测试String类
形参的改变不影响实参的值,实参的值未变:“欢迎来看我的blog”
5.总结结论
- Java是值传递
- 在函数内修改形参的值,不会导致实参的值发生改变
- 若函数的参数类型是引用数据类型,则通过形参修改对象的值会导致实参指向的对象发生变化
- String类表现得像内置数据类型,则通过形参修改对象的值不会导致实参指向的对象发生变化
若有解释有误的地方,欢迎大家指正批评,本人也是个萌新(^ρ^)/
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:jacktools123@163.com进行投诉反馈,一经查实,立即删除!
标签: