首页 > 编程语言 >Java中的参数传递,到底是值传递还是引用传递?

Java中的参数传递,到底是值传递还是引用传递?

时间:2022-08-21 20:00:26浏览次数:102  
标签:参数传递 Java 传递 地址 参数 引用 pass

1、Java中的参数传递,到底是值传递还是引用传递?

结论:Java只有值传递,没有引用传递!

错误理解一:值传递和引用传递,区分的条件是传递的内容,如果是个值,就是值传递。如果是个引用,就是引用传递。
错误理解二:Java是引用传递。
错误理解三:传递的参数如果是普通类型,那就是值传递,如果是对象,那就是引用传递。

2、实参与形参
我们都知道,在Java中定义方法的时候是可以定义参数的。比如Java中的main方法,public static void main(String[] args),这里面的args就是参数。参数在程序语言中分为形式参数和实际参数。

形式参数:是在定义函数名和函数体的时候使用的参数,目的是用来接收调用该函数时传入的参数。
实际参数:在调用有参函数时,主调函数和被调函数之间有数据传递关系。在主调函数中调用一个函数时,函数名后面括号中的参数称为“实际参数”。

简单举个例子:
在这里插入图片描述
实际参数是调用有参方法的时候真正传递的内容,而形式参数是用于接收实参内容的参数。

3、基本类型与引用类型

int num = 10;
String str = "hello";
  • 1
  • 2

在这里插入图片描述
如图所示,num 是基本类型,值就直接保存在变量中。而 str是引用类型,变量中保存的只是实际对象的地址。一般称这种变量为 “引用”,引用指向实际对象,实际对象中保存着内容。

4、赋值运算符“=”的作用

num = 20;
str = "java";
  • 1
  • 2

在这里插入图片描述
对于基本类型 num,赋值运算符会直接改变变量的值,原来的值被覆盖掉。
对于引用类型 str,赋值运算符会改变引用中所保存的地址,原来的地址被覆盖掉。但是原来的对象不会被改变(重要)。
如上图所示,“hello” 字符串对象没有被改变。(没有被任何引用所指向的对象是垃圾,会被垃圾回收器GC回收)

5、值传递与引用传递
上面提到了,当我们调用一个有参函数的时候,会把实际参数传递给形式参数。但是,在程序语言中,这个传递过程中传递的两种情况,即值传递和引用传递。我们来看下程序语言中是如何定义和区分值传递和引用传递的

值传递(pass by value)是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。
引用传递(pass by reference)是指在调用函数时将实际参数的地址直接传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数。

  • 值传递:将参数复制一份,修改形参不会对实参造成影响
  • 引用传递:将实参的地址传递给形参,修改形参也就是在修改实参

我们来测试几段代码:
在这里插入图片描述
上面的代码中,我们在 pass 方法中修改了参数 j 的值,然后分别在 pass 方法和 main 方法中打印参数的值。输出结果如下:

print in pass , j is 20
print in main , i is 10
  • 1
  • 2

可见,pass 方法内部对 name 的值的修改并没有改变实际参数 i 的值。那么,按照上面的定义,有人得到结论:Java 的方法传递是值传递。

但是,很快就有人提出质疑了(哈哈,所以,不要轻易下结论咯。)。然后,他们会给出以下代码:
在这里插入图片描述
同样是一个 pass 方法,同样是在 pass 方法内修改参数的值。输出结果如下:

print in pass , User{name='Tom', sex='man'}
print in main , User{name='Tom', sex='man'}
  • 1
  • 2

经过 pass 方法执行后,实参的值竟然被改变了,那按照上面的引用传递的定义,实际参数的值被改变了,这不就是引用传递了么。于是,根据上面的两段代码,有人得出一个新的结论:Java 的方法中,在传递普通类型的时候是值传递,在传递对象类型的时候是引用传递。但是,这种表述仍然是错误的。不信你看下面这个参数类型为引用类型的参数传递:
在这里插入图片描述

print in pass , Tom
print in main , Mr.Q
  • 1
  • 2

那么,问题来了。String是引用类型,new String(“Mr.Q”)在堆上创建了对象,name指向了Mr.Q的引用。那按照上面来说,应该是引用传递了,输出的结果应该pass和main是相同的,可是,为什么会不同呢?
这又作何解释呢?同样传递了一个对象,但是原始参数的值并没有被修改,难道传递对象又变成值传递了?
其实,是传递的地址值发生了改变
在这里插入图片描述
String类型在值传递和引用传递问题中比较特殊,为什么说特殊呢,因为对于一些常量字符串的创建,只要判断对象在堆中不存在,便会创建一个新的,如果是创建新对象,那么引用地址都会变。我们可以通过一个简单的例子来解释下:
在这里插入图片描述

a是:hello --- b是:你好
  • 1

String a = “hello”; 在 String 池中检查并创建一个常量:“hello”,给 a 分配一个栈内存,在此存储常量 hello 的地址。
String b= a; 给 b 分配一个栈内存,在此存储常量 hello 的地址。相当于 a 把自己持有的地址,复制给了 b。
在这里插入图片描述
b = “你好”; 在 String 池中检查是否有 “你好” 的常量。

  • 如果有,将 b 的地址指向 “你好” 的地址。
  • 如果 String 池中没有 “你好” 常量,在堆内存中创建 “你好” 常量,并将 b 地址指向 “你好”。
    在这里插入图片描述
    我们再来看一个反例,来验证 “Java中参数传递 没有引用传递”
public class Test {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Student s1 = new Student("小张");
        Student s2 = new Student("小李");
        Test.swap(s1, s2);
        System.out.println("s1:" + s1.getName());
        System.out.println("s2:" + s2.getName());
    }

    public static void swap(Student x, Student y) {
        Student temp = x;
        x = y;
        y = temp;
        System.out.println("x:" + x.getName());
        System.out.println("y:" + y.getName());
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

结果:

x:小李
y:小张
s1:小张
s2:小李

方法并没有改变存储在变量 s1 和 s2 中的对象引用。swap 方法的参数 x 和 y 被初始化为两个对象引用的拷贝,这个方法交换的是这两个拷贝。

6、Java中的值传递
值传递和引用传递之前的区别到底是什么?
在这里插入图片描述
两者的最主要区别就是是直接传递的,还是传递的是一个副本
这里我们来举一个形象的例子。再来深入理解一下传值调用和传引用调用:

  • 你有一把钥匙,当你的朋友想要去你家的时候,如果你直接把你的钥匙给他了,这就是引用传递。
  • 这种情况下,如果他对这把钥匙做了什么事情,比如他在钥匙上刻下了自己名字,那么这把钥匙还给你的时候,你自己的钥匙上也会多出他刻的名字。
  • 你有一把钥匙,当你的朋友想要去你家的时候,你复刻了一把新钥匙给他,自己的还在自己手里,这就是值传递。
  • 这种情况下,他对这把钥匙做什么都不会影响你手里的这把钥匙。

那我们再说回到这段代码中:
在这里插入图片描述

print in pass , User{name='Tom', sex='man'}
print in main , User{name='Tom', sex='man'}
  • 1
  • 2

看看在调用中,到底发生了什么?
在这里插入图片描述
在参数传递的过程中,实际参数的地址0x666被拷贝给了形参。这个过程其实就是值传递(这个值,理解为引用的地址),只不过传递的值得内容是对象的应用。

那为什么我们改了user中的属性的值,却对原来的user产生了影响呢?

其实,这个过程就好像是:你复制了一把你家里的钥匙给到你的朋友,他拿到钥匙以后,并没有在这把钥匙上做任何改动,而是通过钥匙打开了你家里的房门,进到屋里,把你家的电视给砸了。

这个过程,对你手里的钥匙来说,是没有影响的,但是你的钥匙对应的房子里面的内容却是被人改动了。

也就是说,Java对象的传递,是通过复制的方式把引用关系传递了,如果我们没有改引用关系,而是找到引用的地址,把里面的内容改了,是会对调用方有影响的,因为大家指向的是同一个共享对象。

那么,如果我们改动一下pass方法的内容:
在这里插入图片描述
上面的代码中,我们在pass方法中,重新new了一个user对象,并改变了他的值,输出结果如下:

print in pass , User{name='Tom'}
print in main , User{name='Mr.Q'}
  • 1
  • 2

也就是说,我把我的钥匙复制给了我的朋友,但是我立马换了我家的锁。因为一new就会在堆上开辟新空间,地址就发生了改变,此时的user不再指向0x666了,理解为我换锁了,朋友当然进不了我家,砸不了电视了。所以此时在pass方法中修改name,不会对我家造成任何影响。
在这里插入图片描述
上面这种传递是什么传递?肯定不是引用传递,如果是引用传递的话,在user = new User()的时候,实际参数的引用也应该改为指向0x999,但是实际上并没有。

通过概念我们也能知道,这里是把实际参数的引用的地址复制了一份,传递给了形式参数。所以,上面的参数其实是值传递,把实参对象引用的地址当做值传递给了形式参数。

所以,值传递和引用传递的区别并不是传递的内容,而是实参到底有没有被复制一份给形参。

在判断实参内容有没有受影响的时候,要看传的的是什么,如果你传递的是个地址,那么就看这个地址的变化会不会有影响,而不是看地址指向的对象的变化。

所以说,Java 中其实还是值传递,只不过对于对象参数,值的内容是对象的引用。

总结
无论是值传递还是引用传递,其实都是一种求值策略 ()。在求值策略中,还有一种叫做按共享传递 (call by sharing)。其实 Java 中的参数传递严格意义上说应该是按共享传递。

按共享传递,是指在调用函数时,传递给函数的是实参的地址的拷贝(如果实参在栈中,则直接拷贝该值)。

在函数内部对参数进行操作时,需要先拷贝的地址寻找到具体的值,再进行操作。

如果该值在栈中,那么因为是直接拷贝的值,所以函数内部对参数进行操作不会对外部变量产生影响。

如果原来拷贝的是原值在堆中的地址,那么需要先根据该地址找到堆中对应的位置,再进行操作。因为传递的是地址的拷贝,所以函数内对值的操作对外部变量是可见的。

简单点说,Java 中的传递,是值传递,而这个值,实际上是对象的引用。

  • 传递的值在栈中,直接拷贝一份值传递,改变的形参不会对实参造成影响
  • 传递的值在栈中存放的是地址(引用),先根据栈中的地址找到在堆上的值,然后把地址拷贝一份(拷贝的地址是一个值),此时形参和实参指向堆上同一个地址,形参的修改导致了实参的改变。

在这里插入图片描述

标签:参数传递,Java,传递,地址,参数,引用,pass
From: https://www.cnblogs.com/Sweetp/p/16610689.html

相关文章

  • 9.Java的LinkedList/Deque相关方法
    Java的LinkedList/Deque中add/offer/push,remove/pop/poll的区别它们来自不同的接口add/remove源自集合,所以添加到队尾,从队头删除;offer/poll源自队列(先进先出=>尾进......
  • Java学习 (26) 异常篇 异常结构&异常处理&自定义异常
    目录异常结构异常的定义异常的分类ErrorExpectionError和Exception的区别具体讲解视频异常处理捕获异常语法实例抛出异常语法实例具体讲解视频自定义异常具体讲解视频异常......
  • JAVA基础--程序流程控制--2022年8月21日
    第一节分支结构1、if分支的写法有几种,各有什么特点?if(){}if(){}  else{}if(){}  elseif(){} elseif(){} ... else{}......
  • 关于java变量的学习
    变量的概念内存中的一个存储区域 该区域的数据可以在同一类型范围内不断变化 变量是程序中最基本的存储单元  包含变量类型、变量名和存储的值使用变量注意J......
  • Java SE 15 新增特性
    JavaSE15新增特性作者:Grey原文地址:JavaSE15新增特性源码源仓库:Github:java_new_features镜像仓库:GitCode:java_new_featuresZGCJavaSE11JEP333将ZGC......
  • IDEA打包普通java项目并用java命令运行
    IDEA下打包为jar包,普通java项目(非web项目)效果是将第三方jar包放到一个文件夹中(如lib),这样看起来清晰一些。如下图这种:  1.项目结构。   1.关键:modules  ......
  • 关于Java 连接 MySQL 数据库报错:Failed to obtain JDBC Connection; ...: Communicati
    原因:是因为Java连接MySQL没有收到任何数据包,也就是说连接失败。解决方法:打开Windows服务程序,找到mysql进程,重启一下就可以了。......
  • 解决DOS系统运行JAVA程序乱码问题
    使用DOS程序运行java程序的时候,碰到乱码问题多数情况是:DOS支持GBK简体中文,不支持UTF-8,只要设置成UTF-8就可以啦。正确显示UTF-8字符,可以按照以下步骤操作:1、打开CMD.exe......
  • 封面 - JavaScript指南
     IT软件开发之JavaScript   AGuidetoJavaScriptJavaScript指南-------------------------------------------------------------------------------争取做......
  • Java 断点下载(下载续传)服务端及客户端(Android)代码
    原文:Java断点下载(下载续传)服务端及客户端(Android)代码-Stars-One的杂货小窝最近在研究断点下载(下载续传)的功能,此功能需要服务端和客户端进行对接编写,本篇也是......