Java中真的只有值传递么?

(本文非引战或diss,只是说出自己的理解,欢迎摆正心态观看或探讨)

回顾值传递和引用传递

关于Java是值传递还是引用传递,网上有不一样的说法。

1、基本类型或基本类型的包装类以及String是值传递,引用类型是引用传递。
2、Java中只有值传递。

关于这个问题应该是存在争议的。根据测试出来的结果和我们自己的经验,以及口口相传或是上学时老师讲的,我们认为是第一种。但第二种说法的呼声也很高,渐渐地我们也认为第2中才是对的。那么下面我们就来分析一下这个问题。

在谈这个问题之前我们先了解值传递和引用传递的概念及现象。我还记得,值传递和引用传递这些概念是大学里学Java的时候老师教给我的,它们的概念是什么呢?老师是通过例子来讲解的,大概是这样的。

值传递

例子1:

 1public static void main(String[] args){
2    TestJavaParamPass() tjpp = new TestJavaParamPass();
3    int num = 10;
4    tjpp.change(num);
5    System.out.println("num in main():"+num);
6}
7public void change(int param){
8    param = 20;
9    System.out.println("param in change():"+param);
10}

控制台输出:

1param in change():20
2num in main():10

mian()方法中的int变量num传递给change()方法,change()方法接收到后将值改变为20。通过看控制台输出,main()方法中的num变量的值没有改变。

结论:基本类型是值传递。

引用传递

例子2:

 1public static void main(String[] args){
2    TestJavaParamPass() tjpp = new TestJavaParamPass();
3    User user = new User();
4    user.setName("Jerry");
5    tjpp.change(user);
6    System.out.println("user in mian():"+user);
7}
8public void change(User param){
9    param.setName("Tom");
10    System.out.println("param in change():"+param);
11}

控制台输出:

1param in change():User(name=Tom}
2user in mian():User(name=Tom}

main()方法中的user变量传递给change()方法,change()方法改变了其name属性值。通过看控制台输出,main()方法中的user变量的name属性值发生改变。

结论:引用类型是引用传递。

特殊的值传递

例子3:

 1public static void main(String[] args){
2    TestJavaParamPass() tjpp = new TestJavaParamPass();
3    String name = "Jerry";
4    tjpp.change(name);
5    System.out.println("name in main():"+name);
6}
7public void change(String param){
8    param = "Tom";
9    System.out.println("param in change():"+param);
10}

控制台输出:

1param in change():Tom
2name in mian():Jerry

String也是引用类型的数据类型,为什么值没改变?因为在change()方法里param = "Tom";相当于param = new String("Tom");就相当于param被重新赋值指向了另外一个对象。所以,其实String类型传的是引用,只不过被重新赋值指向了别的对象了,没有修改原对象。即,String本质上还是引用传递,表像上是值传递。

结论:基本类型是值传递,引用类型是引用传递,String是特殊的值传递。

这个结论也是网络上流传的比较多的,可能大部分程序员的认知都是这样的。至于值传递和引用传递的概念,接下来便可根据上面的例子和结论推断出来,以及解释为什么大多数程序员都将String理解为是特殊的值传递。

概念提取

与其叫概念提取好不如叫结论总结呢。

值传递:基本类型的变量在被传递给方法时,传递的是该变量的值(即复制自己的值传递给方法)。

引用传递:引用类型的变量在被传递给方法时, 传递的是该变量的引用(即自己所指向的内存地址)。

为什么说String是特殊的值传递:是因为String和基本类型从表象来说表现出来的结果是一样,大概是为了便于记忆这个结果才这样说的吧。但是要知道String也是引用传递只不过它的引用被重新赋值,指向了别的对象了,所以不会影响原值。所以String不能简单的说是值传递。

解析Java只有值传递的说法

只有值传递的说法

网上还流传一种说法叫Java只有值传递。网上有文章论证了Java只有值传递的说法,其中举的例子和上面的类似。

分析的很透彻,解释了上面三个例子的本质。指出了上面第二个例子的错误之处,举的例子不恰当。并指出下面这样的例子才恰当,又举了钥匙和房子的例子,佐证了上面第2个例子确实不恰当。因为上面的例子的侧重点都是最后实际变量的值有没有改变。

 1public static void main(String[] args){
2    TestJavaParamPass() tjpp = new TestJavaParamPass();
3    User user = new User();
4    user.setName("Jerry");
5    tjpp.change(user);
6    System.out.println("user in mian():"+user);
7}
8public void change(User param){
9    param = new User()
10    param.setName("Tom");
11    System.out.println("param in change():"+param);
12}

输出:

1param in change():User(name=Tom}
2user in mian():User(name=Jerry}

最后文章的结论是Java只有值传递。引用类型大概是这样解释的( 基本类型就不用说了 ),实际变量(实际参数)赋值一份自己的引用地址的值传给方法,方法的形式参数拿到的是实参的引用地址的值。侧重点在值,所以结论说的是引用类型也是值传递。

解析

我觉得论证者分析基本类型和引用类型的实参形参的变化的原理是没有问题的,但是得出的结论是不是有点不恰当。怎么说呢?请继续看。

论证者的意思是,java只有值传递。也就是说引用类型也是值传递,侧重点是复制一份引用的地址的值给形参,在于这里的值是引用的地址的值(不是引用所指向的内存里存的值),所以说是值传递。是不是有点牵强?我觉得有点偷换概念,没错,大家都知道引用类型传递的是引用的值,但你不能因为传递的是值就说是值传递,不传值还能传什么?引用是内存地址,不是也得用值表示么?

而传统的说法:基本类型是值传递(内存里存东西所代表的值),引用类型是引用传递。我觉得这个侧重点是:基本类型把值复制一份传递过去,引用类型把引用复制一份传递过去。侧重点是传递的东西,基本类型传递的东西叫变量的值(变量本身所代表的值),引用类型传递的东西叫引用(引用本身的值,即内存地址),而非引用所指向的内存空间内的值。所以这样理解引用类型传递的是引用也没问题啊。

所以,Java中基本类型传递的是变量所代表的自身的值(内存里存东西所代表的值),是值传递;引用类型传递的是对象的引用,是引用传递;再深一步,引用也是一个确切的值来表示的,或者你把引用看作是对象的值,那也可以说引用类型传递的是对象的值,是值传递。

文章还说了

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

按共享传递,是指在调用函数时,传递给函数的是实参的地址的拷贝(如果实参在栈中,则直接拷贝该值)。在函数内部对参数进行操作时,需要先拷贝的地址寻找到具体的值,再进行操作。如果该值在栈中,那么因为是直接拷贝的值,所以函数内部对参数进行操作不会对外部变量产生影响。如果原来拷贝的是原值在堆中的地址,那么需要先根据该地址找到堆中对应的位置,再进行操作。因为传递的是地址的拷贝所以函数内对值的操作对外部变量是可见的。

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

这里的意思是,不论是基本类型还是引用类型传给函数的是实参的地址拷贝,也就是内存地址,可以说是引用,只不过基本类型在栈中,函数内对参数操作时直接拷贝的值,引用类型的值在堆中,需要先找到它的位置,即地址、引用。最后说java是值传递,而这个值是对象的引用。

看到这明白了么?

地址就是引用,那是不是可以说java是引用传递了?传递的是引用的值,计算机中不全是值吗,不是值还能是什么,说是引用传递是侧重点不同传,传过去的就是地址就是引用,引用不用值表示用啥

这里说的值不是一个概念,说基本类型传的是值,这个是值变量本身的值,说对象传的也是值,这个值说的是引用是地址,而说对象说是引用传递,侧重点在于说是传的地址,指向对象所代表的内部的属性的地址,非对象所表示的内部的属性的值,为的是和基本类型直接传值区分开。

维基百科:引用 (程序设计)

计算机科学中,引用(英语:reference)是指一个可以让程序间接访问于电脑存储器或其他存储设备中一特定数据的,该数据可以为变量记录
引用和数据本身不同。一般而言,引用会是数据存储于存储器或存储设备中的物理地址。因此,引用亦常被称为该数据的指针地址

看看引用的定义,引用是指一个XXX数据的值。好吧,引用本身就是一个值。但不是值还能是什么呢?计算机中不都是值么?

说值传递还是引用传递都没有错,关键是你怎么定义和解释值传递、引用传递的概念以及值所表示的东西。

计算机中一切皆值,如果从这点出发,那全都是传的值啊,只不过细化到java中,基本类型传递的是自身的值,引用类型传递的是引用的值,而非对象内属性的值。

所以如果武断的说只有值传递也是没问题的,因为在计算机中只能用值来表示啊,但觉得有点投机取巧,就和说世界上只有***,那还区分**和**干嘛,道理差不多。(暂时想不到好的例子哈哈)

还是刚才说的那句,说是引用传递,侧重点在于说是传的是引用是地址,而非对象所表示的内部的属性值,为的是和基本类型直接传值区分开,便于记忆.

最后

最后,大家理解现象的原理即可,没必要追的那么深,或玩文字游戏,钻牛角尖。

如果有人问你,你可以这么说,基本类型和他们的包装类是值传递,引用类型传递的是对象的引用即地址值,String传递的也是地址值,只不过在函数内地址值被修改了,所以不会影响到实参,因表现上和基本类型一样,所以可能为了便于记住这个现象才说String是值传递。归根到底传的都是值只不过值的含义不同。

(本文非引战或diss,只是说出自己的理解,欢迎探讨)

Java中真的只有值传递么?的更多相关文章

  1. Java中到底是值传递还是引用传递?

    Java中到底是值传递还是引用传递? 我们先回顾一下基本概念 实参和形参 参数在编程语言中是执行程序需要的数据,这个数据一般保存在变量中.在Java中定义一个方法时,可以定义一些参数, 举个例子: p ...

  2. 面试题:Java中为什么只有值传递?

    作者:小牛呼噜噜 | https://xiaoniuhululu.com 计算机内功.JAVA底层.面试相关资料等更多精彩文章在公众号「小牛呼噜噜 」 目录 经典的问题 形参&实参 Java是 ...

  3. 2013年6月19日星期三java中函数地址值传递

    今天代码审核时确认了一个问题,理解了java中string和stringbuffer赋值问题,看到一个帖子很好,摘录如下: 理解这两个例子需要分清实参和形参的区别,引用和对象的区别 第一个例子的内部执 ...

  4. java中的参数传递——值传递、引用传递

    参数是按值而不是按引用传递的说明 Java 应用程序有且仅有的一种参数传递机制,即按值传递. 在 Java 应用程序中永远不会传递对象,而只传递对象引用.因此是按引用传递对象.Java 应用程序按引用 ...

  5. java中函数是值传递还是引用传递?

    相信有些同学跟我一样,曾经对这个问题很疑惑.在网上也看了一些别人说的观点,评论不一.有说有值传递和引用传递两种,也有说只有值传递的,这里只说下个人见解 先看一个例子 public class Test ...

  6. Java 参数传递都是值传递

    Java 参数传递都是值传递,验证代码如下 public class ParamTransferTest { public static void swap(int a, int b) { int t ...

  7. java 传参方式--值传递还是引用传递

    java 传参方式--值传递还是引用传递 参数是按值而不是按引用传递的说明 Java 应用程序有且仅有的一种参数传递机制,即按值传递.写它是为了揭穿普遍存在的一种神话,即认为 Java 应用程序按引用 ...

  8. Java的参数传递是值传递还是引用传递?

    一.前言 首先先说结论,Java中方法参数传递方式是按值传递.如果参数是基本类型,传递的是基本类型的字面量值的拷贝.如果参数是引用类型,传递的是该参量所引用的对象在堆中地址值的拷贝. 接下来深入了解一 ...

  9. Java面向对象-方法的值传递和引用传递

    Java面向对象-方法的值传递和引用传递 0 发布时间:『 2016-08-21 14:21』  博客类别:Java核心基础  阅读(197) 评论(0) Java面向对象-方法的值传递和引用传递 方 ...

随机推荐

  1. Condition对象以及ArrayBlockingQueue阻塞队列的实现(使用Condition在队满时让生产者线程等待, 在队空时让消费者线程等待)

    Condition对象 一).Condition的定义 Condition对象:与锁关联,协调多线程间的复杂协作. 获取与锁绑定的Condition对象: Lock lock = new Reentr ...

  2. 【01】主函数main

    java和C#非常相似,它们大部分的语法是一样的,但尽管如此,也有一些地方是不同的. 为了更好地学习java或C#,有必要分清它们两者到底在哪里不同. 首先,我们将探讨主函数main. java的主函 ...

  3. 数据类型-Java基础一-初学者笔记

    初学者笔记 1.Java中的两种类型   在java源代码中,每个变量都必须声明一种类型(type). 有两种类型:primitive type和reference type.引用类型引用对象(ref ...

  4. 【Luogu P1048 Luogu P1016】采药/疯狂的采药

    采药/疯狂的采药 两道模板题,分别是0-1背包和完全背包. 0-1背包 二维:dp[i][j]=max(dp[i-1][j-time[i]]+v[i],dp[i-1][j]); 由于i的状态由i-1的 ...

  5. Linux -- 进程间通信几种方式的总结

    管道 优点 管道文件不占磁盘空间,打开管道时在内存中分配空间: 管道读端会在读取完管道内数据后自动进入阻塞,直到写端再次写入数据: 缺点 管道是半双工的,数据只能从一个方向上流动: 管道大小 PIPE ...

  6. 2019年12月1日Linux开发手记

    配置ubuntu摄像头: 1.设置→添加→usb控制器→兼容usb3.0 2.虚拟机→可移动设备→web camera→连接(断开主机) 3.查看是否配置成功,打开终端,输入: susb ls /de ...

  7. C#异步案例一则

    场景 生产者和消费者队列, 生产者有多个, 消费者也有多个, 生产到消费需要异步. 下面用一个Asp.NetCore Web-API项目来模拟 创建两个API, 一个Get(), 一个Set(), G ...

  8. 你真的了解Object源码吗

    欢迎点赞阅读,一同学习交流,有疑问请留言 . GitHub上也有开源 JavaHouse 欢迎star 引入 Object 应该是比较简单的源码了.现在我们来分析一下他.Object 是类层次结构的根 ...

  9. logistic回归介绍以及原理分析

    1.什么是logistic回归? logistic回归虽然说是回归,但确是为了解决分类问题,是二分类任务的首选方法,简单来说,输出结果不是0就是1 举个简单的例子: 癌症检测:这种算法输入病理图片并且 ...

  10. Xamarin.Forms学习系列之Android集成极光推送

    一般App都会有消息推送的功能,如果是原生安卓或者IOS集成消息推送很容易,各大推送平台都有相关的Sample,但是关于Xamarin.Forms的消息推送集成的资料非常少,下面就说下Xamarin. ...