文章内容为转载,转载自公众号Hollis关于我要彻底给你讲清楚,Java就是值传递,不接受争辩的那种!

  • 原文链接:我要彻底给你讲清楚,Java就是值传递,不接受争辩的那种!
  • 知乎回答链接:Java 到底是值传递还是引用传递?
  • 应届生/社招面试最爱问的几道Java基础问题
  • 知乎回答链接:String str = “Hello”,到底有没有在堆中创建对象?
  • 原文链接:请别再拿“String s = new String(“xyz”);创建了多少个String实例”来面试了吧

文章目录

  • Java中的参数传递,到底是值传递还是引用传递?
    • 实参与形参
    • 基本类型与引用类型
    • 赋值运算符“=”的作用
    • 值传递与引用传递
    • Java中的值传递
  • 总结

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

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

错误理解一:值传递和引用传递,区分的条件是传递的内容,如果是个值,就是值传递。如果是个引用,就是引用传递。

错误理解二:Java是引用传递。

错误理解三:传递的参数如果是普通类型,那就是值传递,如果是对象,那就是引用传递。

实参与形参

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

形式参数:是在定义函数名和函数体的时候使用的参数,目的是用来接收调用该函数时传入的参数。

实际参数:在调用有参函数时,主调函数和被调函数之间有数据传递关系。在主调函数中调用一个函数时,函数名后面括号中的参数称为“实际参数”。

简单举个例子:


实际参数是调用有参方法的时候真正传递的内容,而形式参数是用于接收实参内容的参数。

基本类型与引用类型

int num = 10;
String str = "hello";


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

赋值运算符“=”的作用

num = 20;
str = "java";

对于基本类型 num,赋值运算符会直接改变变量的值,原来的值被覆盖掉。

对于引用类型 str,赋值运算符会改变引用中所保存的地址,原来的地址被覆盖掉。但是原来的对象不会被改变(重要)。

如上图所示,“hello” 字符串对象没有被改变。(没有被任何引用所指向的对象是垃圾,会被垃圾回收器GC回收

值传递与引用传递

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

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

引用传递(pass by reference)是指在调用函数时将实际参数的地址直接传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数。

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

我们来测试几段代码:

上面的代码中,我们在 pass 方法中修改了参数 j 的值,然后分别在 pass 方法和 main 方法中打印参数的值。输出结果如下:

print in pass , j is 20print in main , i is 10

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

但是,很快就有人提出质疑了(哈哈,所以,不要轻易下结论咯。)。然后,他们会给出以下代码:

同样是一个 pass 方法,同样是在 pass 方法内修改参数的值。输出结果如下:

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

经过 pass 方法执行后,实参的值竟然被改变了,那按照上面的引用传递的定义,实际参数的值被改变了,这不就是引用传递了么。于是,根据上面的两段代码,有人得出一个新的结论:Java 的方法中,在传递普通类型的时候是值传递,在传递对象类型的时候是引用传递。

但是,这种表述仍然是错误的。不信你看下面这个参数类型为引用类型的参数传递:

print in pass , Tom
print in main , Mr.Q

那么,问题来了。String是引用类型,new String("Mr.Q")在堆上创建了对象,name指向了Mr.Q的引用。那按照上面来说,应该是引用传递了,输出的结果应该pass和main是相同的,可是,为什么会不同呢?

这又作何解释呢?同样传递了一个对象,但是原始参数的值并没有被修改,难道传递对象又变成值传递了?

其实,是传递的地址值发生了改变

String类型在值传递和引用传递问题中比较特殊,为什么说特殊呢,因为对于一些常量字符串的创建,只要判断对象在堆中不存在,便会创建一个新的,如果是创建新对象,那么引用地址都会变。我们可以通过一个简单的例子来解释下:

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

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 stubStudent 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());}
}

结果:

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

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


Java中的值传递

值传递和引用传递之前的区别到底是什么?

两者的最主要区别就是是直接传递的,还是传递的是一个副本

这里我们来举一个形象的例子。再来深入理解一下传值调用和传引用调用:


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

  • 你有一把钥匙,当你的朋友想要去你家的时候,你复刻了一把新钥匙给他,自己的还在自己手里,这就是值传递。
  • 这种情况下,他对这把钥匙做什么都不会影响你手里的这把钥匙。

那我们再说回到这段代码中:

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

看看在调用中,到底发生了什么?

在参数传递的过程中,实际参数的地址0x666拷贝给了形参。这个过程其实就是值传递(这个值,理解为引用的地址),只不过传递的值得内容是对象的应用。

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

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

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

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

那么,如果我们改动一下pass方法的内容:

上面的代码中,我们在pass方法中,重新new了一个user对象,并改变了他的值,输出结果如下:

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

也就是说,我把我的钥匙复制给了我的朋友,但是我立马换了我家的锁。因为一new就会在堆上开辟新空间,地址就发生了改变,此时的user不再指向0x666了,理解为我换锁了,朋友当然进不了我家,砸不了电视了。所以此时在pass方法中修改name,不会对我家造成任何影响。

上面这种传递是什么传递?肯定不是引用传递,如果是引用传递的话,在user = new User()的时候,实际参数的引用也应该改为指向0x999,但是实际上并没有。

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


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

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

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


总结

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

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

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

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

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

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

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

Java中的参数传递,是值传递! Java只有值传递! Java只有值传递! Java只有值传递!

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

  1. java中的参数传递(只有值传递没有引用传递)

    Java中只有传值调用(值传递),没有传址调用(址传递或者引用传递).所以在java方法中改变参数的值是不会改变原变量的值的,但为什么改变引用变量的属性值却可以呢?请看下面的解答. java中的数据类 ...

  2. Java中的参数传递

    Java中的参数传递:分为值传递和引用传递 但本质上,Java中只有值传递.引用传递,其实可以理解为传的是类似指针的东西. 值传递就是把基本变量的值拷贝一份,传递这个拷贝.引用传递则是传递的引用的地址 ...

  3. java面试题:当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递?

    java面试题:当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递? 答:是值传递.Java编程语言只有值传递参数. 当一个对象实 ...

  4. Java中,String类型和包装类型作为参数传递时,是属于值传递还是引用传递呢?...

    <Java中,String类型和包装类型作为参数传递时,是属于值传递还是引用传递呢?> <Java中的值传递和引用传递> 原理知识: 如果参数类型是原始类型,那么传过来的就是这 ...

  5. Java主方法引用传递_java方法中的参数传递是值传递还是引用传递(转)

    1.当参数变量为基本数据类型或者字符串时: ... ... //定义了一个改变参数值的函数 public static void changeValue(int x) { x = x *2; } .. ...

  6. Java中的值传递和引用传递

    当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递?      答:是值传递.Java 编程语言只有值传递参数.当一个对象实例作为 ...

  7. java是值传递还是引用传递_Java 到底是值传递还是引用传递?

    点赞.收藏还挺多,求一波关注 ------------------------------------------------------------ 没人邀请,自己强行回答一波. 关于这个问题,引发 ...

  8. 一文彻底搞懂Java中的值传递和引用传递!

    关于Java中方法间的参数传递到底是怎样的.为什么很多人说Java只有值传递等问题,一直困惑着很多人,甚至我在面试的时候问过很多有丰富经验的开发者,他们也很难解释的很清楚. 我很久也写过一篇文章,我当 ...

  9. 【Java】探究Java方法的参数传递是值传递还是引用传递

    测试思路 每个更改形参的方法,返回值都是void,不同方法的参数设置不同类型. 注意在方法内测地址的时候在改之前测一下,才能看出传入参数是不是传了地址.(注意反正OS的内存地址是虚拟的,JVM中的也是 ...

最新文章

  1. 三种方法,用Python轻松提取PDF中的全部图片
  2. Java必会的工具库,让你的代码量减少90%
  3. 今日头条告今日油条赔 200 万,各大公司表示不服
  4. java面试常考_JAVA面试常考系列十
  5. 】.NET使用NPOI组件将数据导出Excel
  6. 苹果原生文字转语音播报
  7. java i o是什么流_Java I/O 流,输入流、输出流
  8. 将g++编译器集成到VC2005中
  9. SPI协议的通信原理
  10. VB程序设计教程(第四版)龚沛曾-实验8-3
  11. 用什么手机软件可测试无线信道,wifi信道
  12. 中国需要怎样的智慧城市联盟?中外41家联盟组织大起底
  13. cURL 详解(附实例)
  14. xp计算机用户名和密码忘记了怎么办,电脑的XP系统密码被不小心忘记了怎么办?...
  15. 什么是位移电流?位移电流密度计算公式详解
  16. [大学物理实验-5]波尔共振实验
  17. 【全国计算机等级考试二级教程——C语言程序设计(2021年版)编程题答案-第7章】
  18. 推荐:电脑蓝屏代码分析工具BlueScreenView简体中文版下载
  19. 专访凌云光技术3D视觉业务部经理李淼:做好技术和产品,为VR行业服务
  20. Cracking the Interview 读书笔记 -- Java

热门文章

  1. 图像拼接(十一):双摄像头实时拼接+stitching_detailed
  2. php 读取 excel 文件并上传数据库
  3. 裸函数naked解析
  4. 最近调试HEVC中码率控制, 发现HM里面一个重大bug
  5. 2018 WAIC大咖云集,七牛云“视觉智能,瞳鉴未来”论坛开启在即
  6. 《Java入门》简化的插入排序:本题要求编写程序,将一个给定的整数插到原本有序的整数序列中,使结果序列仍然有序。
  7. 通过git上传个人项目代码到coding.net
  8. Java虚拟机(jvm)——垃圾收集器与内存分配策略
  9. 100000 行级别数据的 Excel 导入优化之路
  10. 服务器定位cpu高占用率代码php,面试官:线上服务器CPU占用率高如何排查定位问题?,...