为什么大家都说Java中只有值传递?
最近跟Java中的值传递和引用传递杠上了,一度怀疑人生。查了很多资料,加上自己的理解,终于搞清楚了,什么是值传递和引用传递。也搞明白了,为什么大家都说Java只有值传递,没有引用传递。原来,我一直以来的认知都是错误的。。。
首先,需要了解一些概念性的东西。
形参与实参:
形参,是指在定义函数时使用的参数,目的是用于接收调用该函数时传入的参数。简单理解,就是所有函数(即方法)的参数都是形参。
实参,是指调用函数时,传递给函数的参数。
public static void main(String[] args) {
int num = 3;
printVal(num); //这里num是实参
}
private static void printVal(int num) {
num = 5; //这里num就是形参
}
值传递和引用传递
值传递:是指在调用函数时,将实际参数复制一份传递给函数,这样在函数中修改参数时,不会影响到实际参数。其实,就是在说值传递时,只会改变形参,不会改变实参。
引用传递:是指在调用函数时,将实际参数的地址传递给函数,这样在函数中对参数的修改,将影响到实际参数。
这里,需要特别强调的是,千万不要以为传递的参数是值就是值传递,传递的是引用就是引用传递。也不要以为传递的参数是基本数据类型就是值传递,传递的是对象就是引用传递。 这是大错特错的。以前的我,一直都是这样认为的,现在想来真是太天真了。判断是值传递还是引用传递的标准,和传递参数的类型是没有一毛钱关系的。
下面三种情况,基本上可以涵盖所有情况的参数类型。
当传递的参数是基本数据类型时:
public class TestNum {
public static void main(String[] args) {
int num = 3;
System.out.println("修改前的num值:"+num);
changeValue(num);
System.out.println("修改后的num值:"+num);
}
private static void changeValue(int num) {
num = 5;
System.out.println("形参num值:"+num);
}
}
打印结果:
修改前的num值:3
形参num值:5
修改后的num值:3
可以发现,传递基本数据类型时,在函数中修改的仅仅是形参,对实参的值的没有影响。
需要明白一点,值传递不是简单的把实参传递给形参,而是,实参建立了一个副本,然后把副本传递给了形参。下面用图来说明一下参数传递的过程:
图中num是实参,然后创建了一个副本temp,把它传递个形参value,修改value值对实参num没有任何影响。
传递类型是引用类型时:
public class User {
private int age;
private String name;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public User(int age, String name) {
this.age = age;
this.name = name;
}
public User() {
}
@Override
public String toString() {
return "User{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
}
public class TestUser {
public static void main(String[] args) {
User user = new User(18, "zhangsan");
System.out.println("修改对象前:"+user);
changeUser(user);
System.out.println("修改对象后:"+user);
}
private static void changeUser(User user) {
user.setAge(20);
user.setName("lisi");
}
}
打印结果:
修改对象前:User{age=18, name='zhangsan'}
修改对象后:User{age=20, name='lisi'}
可以发现,传过去的user对象,属性值被改变了。由于,user对象存放在堆里边,其引用存放在栈里边,其参数传递图如下:
user是对象的引用,为实参,然后创建一个副本temp,把它传递给形参user1。但是,他们实际操作的都是堆内存中的同一个User对象。因此,对象内容的修改也会体现到实参user上。
传递类型是String类型(Integer等基本类型的包装类等同)
public class TestStr {
public static void main(String[] args) {
String str = new String("zhangsan");
System.out.println("字符串修改前:"+str);
changeStr(str);
System.out.println("字符串修改后:"+str);
}
private static void changeStr(String str) {
str = "lisi";
}
}
打印结果:
字符串修改前:zhangsan
字符串修改后:zhangsan
咦,看到这是不是感觉有点困惑。按照第二种情况,传递参数是引用类型时,不是可以修改对象内容吗,String也是引用类型,为什么在这又不变了呢?
再次强调一下,传递参数是引用类型,并不代表就是引用传递,其实它还是值传递。此时的 lisi 和上边的 zhangsan 根本不是同一个对象。画图理解下:
图中,str是对象 zhangsan 的引用,为实参,然后创建了一个副本temp,把它传递给了形参str1。此时,创建了一个新的对象 lisi ,形参str1指向这个对象,但是原来的实参str还是指向zhangsan。因此,形参内容的修改并不会影响到实参内容。所以,两次打印结果都是zhangsan。
第三种情况和第二种情况虽然传递的都是引用类型变量,但是处理方式却不一样。第三种情况是创建了一个新的对象,然后把形参指向新对象,而第二种情况并没有创建新对象,操作的还是同一个对象。如果把上边changeUser方法稍作改变,你就会理解:
private static void changeUser(User user) {
//添加一行代码,创建新的User对象
user = new User();
user.setAge(20);
user.setName("lisi");
}
运行以上代码,你就会惊奇的发现,最终打印修改前和修改后的内容是一模一样的。
这种情况,就等同于第三种情况。因为,这里的形参和实参引用所指向的对象是不同的对象。因此,修改形参对象内容并不会影响实参内容。
修改对象前:User{age=18, name='zhangsan'}
修改对象后:User{age=18, name='zhangsan'}
总结:
从以上三个例子中,我们就能理解了,为什么Java中只有值传递,并没有引用传递。值传递,不论传递的参数类型是值类型还是引用类型,都会在调用栈上创建一个形参的副本。不同的是,对于值类型来说,复制的就是整个原始值的复制。而对于引用类型来说,由于在调用栈中只存储对象的引用,因此复制的只是这个引用,而不是原始对象。
最后,再次强调一下,传递参数是引用类型,或者说是对象时,并不代表它就是引用传递。引用传递不是用来形容参数的类型的,不要被“引用”这个词本身迷惑了。这就如同我们生活中说的地瓜不是瓜,而是红薯一样。
- 参数传递时,是拷贝实参的副本,然后传递给形参。(值传递)
- 在函数中,只有修改了实参所指向的对象内容,才会影响到实参。以上第三种情况修改的实际上只是形参所指向的对象,因此不会影响实参。
为什么大家都说Java中只有值传递?的更多相关文章
- 为什么说Java中只有值传递
本文转载自公众号 Hollis 对于初学者来说,要想把这个问题回答正确,是比较难的.在第二天整理答案的时候,我发现我竟然无法通过简单的语言把这个事情描述的很容易理解,遗憾的是,我也没有在网上找到哪篇文 ...
- java中的值传递和引用传递有什么区别呀?
值传递: (形式参数类型是基本数据类型和String):方法调用时,实际参数把它的值传递给对应的形式参数,形式参数只是用实际参数的值初始化自己的存储单元内容,是两个不同的存储单元,所以方法执行中形式参 ...
- 为什么说Java中只有值传递(转载)
出处:https://www.hollischuang.com/archives/2275 关于这个问题,在StackOverflow上也引发过广泛的讨论,看来很多程序员对于这个问题的理解都不尽相同, ...
- 为什么说Java中只有值传递?
一.为什么说Java中只有值传递? 对于java中的参数传递方式中是否有引用传递这个话题,很多的人都认为Java中有引用传递,但是我个人的看法是,Java中只有值传递,没有引用传递. 那么关于对象的传 ...
- 为什么说Java中只有值传递----说服自己
在开始深入讲解之前,有必要纠正一下大家以前的那些错误看法了.如果你有以下想法,那么你有必要好好阅读本文. 错误理解一:值传递和引用传递,区分的条件是传递的内容,如果是个值,就是值传递.如果是个引用,就 ...
- Java中只有值传递,(及值传递与引用传递详解)
首先呢,我们来说一下值传递与引用传递的区别(这两个玩意儿实在调用函数的时候提到的) 比如说 code( a) code( int a ) code(a)是调用函数,a是我们原本函数的一个值类型,然后使 ...
- 为什么说 Java 中只有值传递?
对于初学者来说,要想把这个问题回答正确,是比较难的.在第二天整理答案的时候,我发现我竟然无法通过简单的语言把这个事情描述的很容易理解,遗憾的是,我也没有在网上找到哪篇文章可以把这个事情讲解的通俗易懂. ...
- 为什么Java中只有值传递
原文链接:https://www.cnblogs.com/wchxj/p/8729503.html 在开始深入讲解之前,有必要纠正一下大家以前的那些错误看法了.如果你有以下想法,那么你有必要好好阅读本 ...
- 转:为什么说Java中只有值传递
原文:https://www.cnblogs.com/wchxj/p/8729503.html 错误理解 在开始深入讲解之前,有必要纠正一下大家以前的那些错误看法了.如果你有以下想法,那么你有必要好好 ...
随机推荐
- Getting started with the basics of programming exercises_5
1.编写函数,把由十六进制数字组成的字符串转换为对应的整型值 编写函数htoi(s),把由十六进制数字组成的字符串(包含可选的前缀0x或0X)转换为与之等价的整型值.字符串中允许包含的数字包括:0~9 ...
- hdu 3662 3D Convex Hull
Problem - 3662 题意很简单,构造三维凸包,求凸包有多少个面. 代码如下: #include <cstdio> #include <iostream> #inclu ...
- PHP 试题(1)
1.__FILE__表示什么意思?(5分)文件的完整路径和文件名.如果用在包含文件中,则返回包含文件名.自 PHP 4.0.2 起,__FILE__ 总是包含一个绝对路径,而在此之前的版本有时会包含一 ...
- 2018-9-28-WPF-自定义-TextBoxView-的-Margin-大小
title author date CreateTime categories WPF 自定义 TextBoxView 的 Margin 大小 lindexi 2018-09-28 17:16:17 ...
- Spring Boot 动态数据源(多数据源自动切换)
本文实现案例场景: 某系统除了需要从自己的主要数据库上读取和管理数据外,还有一部分业务涉及到其他多个数据库,要求可以在任何方法上可以灵活指定具体要操作的数据库. 为了在开发中以最简单的方法使用,本文基 ...
- js基础-原型
1.定义:我们创建的函数都有一个prototype(原型)属性,该属性是一个对象, 原型模式声明中多了两个属性(自动生成). 构造函数: function Box(nam ...
- mpvue的坑,持续更新-.-
mpvue... 坑 怎么说呢,去github看一下,发现还是有很多问题没有解决... 不支持filter 亲,到现在还没有支持filter哦.只能用替代方法了,用computed或者渲染前先处理数据 ...
- H3C调试信息输出的例子
- Scala中的函数表达式
最近看Spark的东西,由于之前没有接触过lambda函数表达式,所以搜了点资料,特地纪录在此 Scala中的Lambda表达式 在函数式编程中,函数是基本的构造块.Scala融合了java中的面向对 ...
- dotnet 将文件删除到回收站
默认删除文件的时候 File.Delete 是将文件永久删除,如果是一些文档,建议删除到回收站,这样用户可以自己还原 通过 SHFileOperation 可以将文件放在回收站 本文提供的方法暂时只能 ...