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. nyoj 41-三个数从小到大排序(STL --> sort(a, a+n) 升序)

    41-三个数从小到大排序 内存限制:64MB 时间限制:3000ms Special Judge: No accepted:31 submit:44 题目描述: 现在要写一个程序,实现给三个数排序的功 ...

  2. CSS RESET —— 浏览器样式重置

    CSS Reset 1. CSS Reset为什么存在? 只要您的客户存在使用不同浏览器(ie,firefox,chrome等)的可能,那你就不得不从完美的理想状态回到现实,因为不同核心的浏览器对CS ...

  3. HTML的标签认识

    <!--      html标签     h1~h6 标题标签(只有1~6,依次减小)     p 段落标签     span 无意义的行标签     div 无意义的块标签     b 加粗 ...

  4. spring boot集成shiro-redis时,分布式根据seesionId获取session报错排查总结

    昨天在集成shiro-redis的时候,使用sessionId在其他微服务获取用户的session时,发生错误:There is no session with id [xxx]. 查遍了所有资料,基 ...

  5. Springboot 系列(十六)你真的了解 Swagger 文档吗?

    前言 目前来说,在 Java 领域使用 Springboot 构建微服务是比较流行的,在构建微服务时,我们大多数会选择暴漏一个 REST API 以供调用.又或者公司采用前后端分离的开发模式,让前端和 ...

  6. php如何处理大数据高并发

    大数据解决方案 使用缓存: 使用方式:1,使用程序直接保存到内存中.主要使用Map,尤其ConcurrentHashMap. 使用缓存框架.常用的框架:Ehcache,Memcache,Redis等. ...

  7. android 网络异步加载数据进度条

    ProgressDialog progressDialog = null; public static final int MESSAGETYPE = 0; private void execute( ...

  8. 2019-10-30:渗透测试,基础学习,mssql堆叠内联注入,mongodb基础语法

    使用xp_cmdshell需要堆叠注入,http://192.168.190.148/less-1.asp?id=1';EXEC sp_configure 'show advanced options ...

  9. 2019-9-19:渗透测试,HTML基础学习,html绘制表格

    1,受理员业务统计表 效果图: 代码: <!DOCTYPE html><html><head> <title>表格1</title>< ...

  10. X86架构CPU常识(主频,外频,FSB,cpu位和字长,倍频系数,缓存,CPU扩展指令集,CPU内核和I/O工作电压,制造工艺,指令集,超流水线与超标量)

    1.主频 主频也叫时钟频率,单位是MHz,用来表示CPU的运算速度. CPU的主频=外频×倍频系数.很多人认为主频就决定着CPU的运行速度,这不仅是个片面的,而且对于服务器来讲,这个认识也出现了偏差. ...