java的传值调用什么_Java的传值调用

2023-11-06

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

引子

之所以写这篇文章是因为前些天写了一篇《Java中真的只有值传递么?》探讨了网上关于Java只有值传递的说法,当时写这篇文章的缘由是因为之前看的文章讲解的Java只有值传递,讲的不是让我很明白,没有拿出比较专业的解释或定义,没有说服我。而我在《Java中真的只有值传递么?》这篇文章中又做了一些解读,发现自己也是没有抓住重点,这才有了今天这篇文章,对之前的这篇文章做一个补充。

从那篇文章后,我了解到Java的参数传递其实牵涉到了Java语言的设计中的参数传递方式,可能在语言设计之时就考虑了这个问题,所以在工作之余自己简单的研究了一下,最终也能根据自己的理解解释一下关于Java是值传递还是引用传递的说法。

Java 是引用传递还是值传递现在有以下这些说法:

1、值传递和引用传递,区分的条件是传递的内容,如果是个值,就是值传递。如果是个引用,就是引用传递。

2、传递的参数如果是普通类型,那就是值传递,如果是对象,那就是引用传递。

3、Java中只有值传递。

关于这个问题应该是分情况讨论的,存在即合理,或许在不同的认识下有不同的说法,也不能简单的就说是值传递还是引用传递。

对或错都是相对的。

回顾

在谈这个问题之前我们先了解下值传递和引用传递的概念及现象。可以简单的通过几个例子来讲解的,大概是这样的。

值传递

例子1:

public static void main(String[] args){

TestJavaParamPass() tjpp = new TestJavaParamPass();

int num = 10;

tjpp.change(num);

System.out.println("num in main():"+i);

}

public void change(int param){

param = 20;

System.out.println("param in change():"+param);

}

控制台输出:

param in change():20

num in main():10

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

结论:实参没有被形参影响,基本类型是值传递。

引用传递

例子2:

public static void main(String[] args){

TestJavaParamPass() tjpp = new TestJavaParamPass();

User user = new User();

user.setName("Jerry");

tjpp.change(user);

System.out.println("user in mian():"+user);

}

public void change(User param){

param.setName("Tom");

System.out.println("param in change():"+param);

}

控制台输出:

param in change():User(name=Tom}

user in mian():User(name=Tom}

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

结论:形参变了实参也变了,引用类型是引用传递。

特殊的值传递

例子3:

public static void main(String[] args){

TestJavaParamPass() tjpp = new TestJavaParamPass();

String name = "Jerry";

tjpp.change(name);

System.out.println("name in main():"+i);

}

public void change(String param){

param = "Tom";

System.out.println("param in change():"+param);

}

控制台输出:

param in change():Tom

name in mian():Jerry

String也是引用类型的数据类型,为什么值没改变?

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

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

看到这样的结论,没有去深究过,可能大部分程序员的认知都是这样的。

根据上面的例子我们先初步给值传递和引用传递下个定义,以及解释为什么大多数程序员都将String理解为是特殊的值传递。

概念提取

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

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

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

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

而仅仅根据上面的实验就给值传递,引用传递下这样的结论是不是太草率了?

解析

对于文章开始时提到的那些说法,前两种可以这样解释:

大概是因为int没有因为change方法而改变原值,所以就说它传过去的是自身的值,因而叫值传递;User对象经过change方法后,对象的数据变了,就认为是因为实参和形参指向的是同一片内存空间,内存空间的数据变了就都变了,传过去的是引用所以就说对象是引用传递。这样说的侧重点是传递的东西。

所以,如果从传递的东西的角度来看这两种说法也是没问题的呀。

至于Java只有值传递的说法,我查阅了一些资料结合网上的文章了解到了求值策略这个名词,这大概牵涉到了语言本身的设计。所以就从这些名词来探究Java的方法调用时参数传递的奥秘。

我们先来看看这些编程语言里关于参数传递函数调用有关的术语。

(以下术语来自Wiki )

求值策略(Evaluation strategy)

在计算机科学中,求值策略(英语:Evaluation strategy)是确定编程语言中表达式的求值的一组(通常确定性的)规则。 重点典型的位于函数或算子上——求值策略定义何时和以何种次序求值给函数的实际参数,什么时候把它们代换入函数,和代换以何种形式发生。

求值策略:是一组求值规则,用来定义如何为函数的实际参数求值。它是用来规定程序语言在方法、函数或过程调用时的传参策略,是在程序语言设计时就应该考虑的问题。而下面的这几个调用方式都属于求值策略。

传值调用(Call by value)

“传值调用”求值是最常见的求值策略,C和Scheme这样差异巨大的语言都在使用。在传值调用中实际参数被求值,其值被绑定到函数中对应的变量上(通常是把值复制到新内存区域)。如果函数或过程能把值赋给它的形式参数,则被赋值的只是局部拷贝——就是说,在函数返回后调用者作用域里的曾传给函数的任何东西都不会变。

传值调用不是一个单一的求值策略,而是指一类函数的实参在被传给函数之前就被求值的求值策略。 尽管很多使用传值调用的编程语言(如Common Lisp、Eiffel、Java)从左至右的求值函数的实际参数,某些语言(比如OCaml)从右至左的求值函数和它们的实际参数,而另一些语言(比如Scheme和C)未指定这种次序(尽管它们保证顺序一致性)。

传值调用:在传值调用中,实际参数被求值后传递给被调函数。也就是说传值调用是实参在被传给函数之前就被求值的一种求值策略。

在Java中的体现

那什么叫实参在被传给函数之前就被求值呢?求的是谁的值呢?这个值又是什么呢?是怎么求得呢?

带着这些疑问,我们来看下面的例子。

如下,在调用change()方法时实参为i,当程序执行到change(i)这一行时,i是实参,这时i就要被求值了,会求出i的值即4传给change()方法;change()的形参a拿到的是实参i的值,是一个拷贝副本。

伪代码:

void change(int a){//拿到求得的实参的值

a = a/2;

}

int i = 4;

change(i);

System.out.print(i);

因为是值的副本,所以在函数内对形参操作不会影响实参,所以输出是4。

这里我们举的例子是基本类型int类型的。那对于引用类型呢?

同样需要对实参求值,这时得到的值是实参的地址值,形参拿到的是实参的地址值,这个地址值指向的是u1等号后面使用new关键字开辟出来的那片内存空间,所以此时u2也指向这片内存空间,所以打印出来u2将会和u1输出同样的内容。

伪代码:

void change(User u2){//拿到求得的实参的地址值

System.out.print(u2);

u2 = getNewUser();

u2.setName("$%#@*")

System.out.print(u2);

}

public static void main(){

User u1 = new User();

u1.setName("1234");

change(u1);

System.out.print(u1);

}

然后,我们模仿上面的change(int a)的方法里,对形参接收到的值进行改变。注意,是形参的值,对change(User u2)来说,形参u2接收到的值是地址值,我们咋改变它呢?我们可以让u2指向另一个内存空间,即通过getNewUser()方法获取一个新的User对象,用这种方式给u2一个新的地址值,这不就改变了吗。

此时我们看输出,发现经过change()方法实参u1打印信息没变,为什么?因为u1的地址值没变,且u2是获得新地址后(指向另一片内存),在新的这片内存里操作的,故而不会影响到之前的那片内存空间的数据。

这样基本类型和引用类型的实验方法是一样的,看到的效果也是一样的,即实参没有随形参的改变而改变。

总结

最后得出的结论:从语言设计的角度,Java的方法调用时参数的求值策略是传值调用(Call by value)的。

如果我们想表达引用类型传递的是引用,仅仅是想说传的是引用不是别的东西的话,我们可以说的明确点:引用类型传的是引用,和程序语言中的求值策略不沾边 。那你说的引用传递就和求值策略中的传引用调用没关系,只是想表达传的是引用的话也没人会说你错。由此来看文章开头提到的前2种说法是不是也有解释的余地?

存在即合理,不同的说法有不同的前提条件不同的解释方式。如果是从程序语言设计的求值策略角度来问Java是哪种求值策略的话,那可以肯定的说是传值调用(Call by value)。

(以下术语摘抄自Wiki。能力有限,对这样些专业名词还无法完美解读,仅供参考)

附录

传引用调用和传共享对象调用都是求值策略的一种。

传引用调用(Call by reference)

在“传引用调用”求值中,传递给函数的是它的实际参数的隐式引用而不是实参的拷贝。通常函数能够修改这些参数(比如赋值),而且改变对于调用者是可见的。因此传引用调用提供了一种调用者和函数交换数据的方法。传引用调用的语言中追踪函数调用的副作用比较难,易产生不易察觉的bug。

很多语言支持某种形式的传引用调用,但是很少有语言默认使用它。FORTRAN II 是一种早期的传引用调用语言。一些语言如C++、PHP、Visual Basic .NET、C#和REALbasic默认使用传值调用,但是提供一种传引用的特别语法。

在那些使用传值调用又不支持传引用调用的语言里,可以用引用(引用其他对象的对象),比如指针(表示其他对象的内存地址的对象)来模拟。C和ML就用了这种方法。这不是一种不同的求值策略(语言本身还是传值调用)。它有时被叫做“传地址调用”(call by address)。这可能让人不易理解。在C之类不安全的语言里会引发解引用空指针之类的错误。但ML的引用是类型安全和内存安全的。

类似的效果可由传共享对象调用(传递一个可变对象)实现。比如Python、Ruby。

例:C用指针模拟的传引用调用

void modify(int p, int* q, int* r){

p = 27; // passed by value: only the local parameter is modified

*q = 27; // passed by value or reference, check call site to determine which

*r = 27; // passed by value or reference, check call site to determine which

}

int main(){

int a = 1;

int b = 1;

int x = 1;

int* c = &x;

modify(a, &b, c); // a是传值调用, b通过创建指针实现引用传递,c是按值传递的指针

//b and x are changed

return 0;

}

传共享对象调用(Call by sharing)

此方式由Barbara Liskov命名[1],并被Python、Java(对象类型)、JavaScript、Scheme、OCaml等语言使用。

与传引用调用不同,对于调用者而言在被调用函数里修改参数是没有影响的。如果要达成传引用调用的效果就需要传一个共享对象,一旦被调用者修改了对象,调用者就可以看到变化(因为对象是共享的,没有拷贝)。比如这段Python代码:

def f(l):

l.append(1)

l = [2]

m = []

f(m)

print(m)

会输出[1]而不是[2]。因为列表是可变的,append方法改变了m。而赋值局部变量l的行为对外面作用域没有影响(在这类语言中赋值是给变量绑定一个新对象,而不是改变对象)。

使用C/C++语言的程序员可能因不能用指针等使函数返回多个值而感到不便,但是像Python这样的语言提供了替代方案:函数能方便的返回多个值,比C++11的std::tie更加简单。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

java的传值调用什么_Java的传值调用 的相关文章

  • 【杨氏矩阵】

    文章目录 前言 一 题目描述 二 题目解析 一 解法1 二分查找 二 解法2 Step wise线性搜索解法 总结 前言 大家好 我是熊猫 今天要和大家一起学习的是在杨氏矩阵中寻找数字的问题 一 题目描述 有一个数字矩阵 矩阵的每行从左到右
  • centos和ubantu安装软件的区别

    序言 安装软件时经常会遇到类似下面这张图 那这些不同的Linux版本有哪些区别 安装软件又应该注意哪些 本文将就以下问题展开讨论 Linux发行版本有哪些 Linux不同版本安装软件的方式和区别 说明 图中FreeBSD Oracle So
  • 小技巧:如何在R语言与excel/word之间进行复制粘贴

    原创 康哥 勤用统计 问 R语言中能进行类似电脑中control C control V的操作吗 现实数据处理过程中 经常需要进行R语言与Excel word等文件的数据传输 笨方法 是直接导出or导入整个文件 答 R语言也可以与Excel
  • [蓝桥杯][算法提高VIP]我们的征途是星辰大海

    题目 题目链接 题解 实现题 这也很基础 写代码的时候细心点就行 代码 include
  • 时序预测

    时序预测 MATLAB实现PSO BP时间序列预测 粒子群优化BP神经网络时间序列预测 多指标评价 目录 时序预测 MATLAB实现PSO BP时间序列预测 粒子群优化BP神经网络时间序列预测 多指标评价 效果一览 基本介绍 程序设计 参考
  • 动态路由协议

    动态路由协议 在各台路由器上 激活同一种协议后 路由器间沟通计算获取未知路由信息 最终生成路由表实现全网可达 静态协议的缺点 1 在中大型网络中配置量大 2 不能实时收敛 不能基于拓扑的变化而变化 动态协议的优点 1 在中大型的配置量较静态
  • CRM-统计分析--线索统计--新增线索数量折线图(接口实现)

    统计分析 线索统计 新增线索数量折线图 需求 统计出一段时间内的每一天 新增的线索数量 通过每天新增的线索数量和线索总数量 分析线上线下活动的执行情况 难度级别 B级 接口名 report salesStatistics 请求方式 get请
  • 个人免签支付云端监听免挂机支付宝收款

    GOGO支付 打不开了 貌似liangle 然后自己根据原理实现了一套 方案用来替代 gogo支付收款 云端监听免挂机 支付宝采用抓包技术云端调用官方接口 获取收款信息 监听效率非常高 而且很稳定 GOGO支付个人免签支付系统实现原理说明
  • 让flexmojos modulefiles支持通配符文件集,模块输出不带版本号且按包结构输出

    jar包下载 http download csdn net source 1879817 1 从http svn sonatype org flexmojos tags flexmojos 3 2 0 check out 源码 2 修改fl
  • 《Attention Is All You Need》

    论文地址 https arxiv org abs 1706 03762 谷歌于2017年发布论文 Attention Is All You Need 提出了一个只基于attention的结构来处理序列模型相关的问题 比如机器翻译 相比传统的
  • 什么是Base64

    一 什么是Base64 百度百科中对Base64有一个很好的解释 Base64是网络上最常见的用于传输8Bit字节码的编码方式之一 Base64就是一种基于64个可打印字符来表示二进制数据的方法 什么是 可打印字符 呢 为什么要用它来传输8
  • logback mdc日志跟踪

    1 简介 MDC Mapped Diagnostic Context 映射调试上下文 是 log4j logback及log4j2 提供的一种方便在多线程条件下记录日志的功能 MDC 可以看成是一个与当前线程绑定的哈希表 可以往其中添加键值
  • 图象恢复——(逆滤波,维纳滤波)

    目的 对获取图像在频域用高斯函数进行退化并叠加白噪声 对退化图像进行逆滤波和维纳滤波恢复 比较原始图像和恢复图像 对利用逆滤波和维纳滤波恢复方法恢复图像进行比较 一 基本原理 图像复原是一种客观的操作 通过使用退化现象的先验知识重建或恢复一
  • Windows上Kafka运行环境安装

    1 安装JDK 1 1 安装文件 http www oracle com technetwork java javase downloads index html 下载JDK 1 2 安装完成后需要添加以下的环境变量 右键点击 我的电脑 g
  • Daily Scrum: 2012/11/12

    由于我们是从10月31日开始进行Daily Scrum的 所以我们的Daily Scrum时间段为10 31 11 12共10天 包括一天周六 成员 角色 今天工作 明天计划 王安然 PM Dev 完成了EnermyCraft抽象类 并进行
  • 毕业设计-基于深度学习的命名实体识别研究

    目录 目录 前言 课题背景和意义 实现技术思路 一 命名实体识别简单概述 二 基于深度学习的命名实体识别方法 实现结果 最后 前言 大四是整个大学期间最忙碌的时光 一边要忙着备考或实习为毕业后面临的就业升学做准备 一边要为毕业设计耗费大量精
  • 打包第三库那些事

    介绍 一般来说 写完一个第三方库需要打包出三个文件夹的文件 对应三种不同模块类型 outputpath dist umd module es es module lib commonjs module 三个模块类型 umd UMD Univ
  • Springboot使用Knife4j

    简述 knife4j是为Java MVC框架集成Swagger生成Api文档的增强解决方案 knife4j的前身是 swagger bootstrap ui 为了契合微服务的架构发展 由于原来 swagger bootstrap ui采用的
  • eclipse opengl java_eclipse安装openGL方法(完整版)

    学校上机使用在Windows上开发OpenGL 一般都会选择Visual Studio作为开发工具 不过我更喜欢Eclipse 在Windows上开发OpenGL所需的库一般会带有32这个后缀 跟Linux上的还不太一样 1 首先下载Ecl

随机推荐

  • 移入——归约技术

    归约 定义 我们可以将自底向上语法分析过程看成是建一个串w 归约 慰问发开始符号的过程 在归约中 一个与某产生式体相匹配的特定子串被替换为该产生式的头部的非终结符号 定义理解起来比较晦涩 我们来看个例子就知道了 已知有文法 E gt E T
  • SpringBoot slf4j的yaml日志配置不生效

    Spring boot工程中使用slf4j日志框架 发现日志配置总是不生效 我的yaml配置如下 logging level 全局日志级别 root info 具体到某个类的日志级别 打印所有访问请求日志 com xyz filter We
  • specialization of template.... in different namespace的解决

    代码来自DTL文档index htm struct Example tablename columnname int exampleInt DB EXAMPLE INT VALUE string exampleStr DB EXAMPLE
  • 理论总结

    作业1 2 1 Python程序是区分大小写的 2 解释程序对高级语言编写的程序是一边翻译 一边执行的 下次执行同样的程序时 还必须重新翻译 3 Python是一种用途广泛 解释型 面向对象的程序设计语言 4 程序设计语言包括机器语言 汇编
  • Into Clause VS Let Clause

    1 Into Clause 用来将select join 或者group的结果存储到一个临时变量中 目的 在之后的查询中需要使用此结果 例如 var developersGroupedByLanguage from d in develop
  • 面了一个测试工程师,明显感觉他背了很多面试题...

    最近有朋友去字节面试 面试前后进行了20天左右 包含4轮电话面试 1轮笔试 1轮主管视频面试 1轮hr视频面试 据他所说 80 的人都会栽在第一轮面试 要不是他面试前做足准备 估计都坚持不完后面几轮面试 其实 第一轮的电话面试除了一些常规的
  • LRU缓存淘汰算法

    概念理解 1 LRU是Least Recently Used的缩写 即最近最少使用页面置换算法 是为虚拟页式存储管理服务的 2 操作系统课程里有学过 在内存不够的场景下 淘汰就内容的策略 淘汰掉最不经常使用 LRU原理 可以用一个特殊的栈来
  • CentOS 7安装Gnome GUI 图形界面

    http www centoscn com image text config 2015 0528 5552 html
  • jwt在线解密工具分享

    前言 之前调用一个第三方api的时候 看到需要在Authorization填写bearer token 英文不好 看成了熊 bear 心里很疑惑 实际上 bearer 指的是持票人 Bearer Token用于授权访问资源 任何Bearer
  • splunk之获取数据(Ingesting Data)

    Ingesting Data 下载数据地址 http splk it f1data use uname in the Username field and 5p1unkbcup for the Password field
  • GDB调试命令详解

    GDB是什么 调试程序 程序中出现的语法错误可以借助编译器解决 但逻辑错误则只能靠自己解决 实际场景中解决逻辑错误最高效的方法 就是借助调试工具对程序进行调试 所谓调试 Debug 就是让代码一步一步慢慢执行 跟踪程序的运行过程 比如 可以
  • Vue如何实现反向代理(配置proxy)

    Vue如何实现反向代理 那问题来了 反向代理是什么 反向代理 Reverse Proxy 实际运行方式是指以代理服务器来接受internet上的连接请求 然后将请求转发给内部网络上的服务器 并将从服务器上得到的结果返回给internet上请
  • Mysql的B+树高度计算

    问题 假设B 树的高度是2 一行数据的记录大小是1K 主键ID是int类型 问 该B 树存放的总记录数 知识点 Mysql的默认存储引擎是Innodb Innodb的最小存储单位是页 一页大小等于16K B 树的叶子节点存放数据 内部节点存
  • 黑马 Spring_day01

    Spring day01 今日目标 掌握Spring相关概念 完成IOC DI的入门案例编写 掌握IOC的相关配置与使用 掌握DI的相关配置与使用 1 课程介绍 对于一门新技术 我们需要从为什么要学 学什么以及怎么学这三个方向入手来学习 那
  • 计算机提示d3dcompiler43.dll缺失怎么修复,多个解决方法分享

    在游戏玩家中 遇到游戏提示找不到 d3dcompiler43 dll 文件的情况并不罕见 这使得许多玩家在启动游戏时感到困扰 因为这意味着他们可能无法正常运行游戏 那么 d3dcompiler43 dll 文件到底是什么呢 为什么游戏会提示
  • 数据库实现学生管理系统

    1 QT将数据库分为三个层次 1 gt 数据库驱动层 QSqlDriver QSqlDriverCreator QSqlDriverCreatorBase QSqlDriverPlugin 2 gt sql接口层 QSqlDatabase
  • 在剪贴板上有大量信息,是否保留其内容, 以便此后粘贴到其他程序中? VBA 对策

    在剪贴板上有大量信息 是否保留其内容 以便此后粘贴到其他程序中 对策a 是文件关闭前 随便复制一个空单元格就可以了 对策b Application DisplayAlerts False 关闭任何提醒 但复制的信息将仍旧保存在剪贴板中 对策
  • Qt数据库编程

    Qt数据库编程 版本说明 版本 作者 日期 备注 0 1 loon 2018 10 25 初稿 目录 文章目录 Qt数据库编程 版本说明 目录 一 需求和目的 二 使用说明 1 Qt 5 6 0的数据库操作类 2 Driver Layer
  • 堆栈指针SP

    1 什么是堆栈指针 在51系列单片机里 堆栈指针sp在片内RAM128B中开辟栈区 并随时跟踪栈顶地址 它是按 先进后出 的原则存取数据 开机复位后 单片机栈底地址为07H 主要用来保存临时数据 局部变量和中断 调用子程序程序的返回地址 堆
  • java的传值调用什么_Java的传值调用

    本文非引战或diss 只是说出自己的理解 欢迎摆正心态观看或探讨 引子 之所以写这篇文章是因为前些天写了一篇 Java中真的只有值传递么 探讨了网上关于Java只有值传递的说法 当时写这篇文章的缘由是因为之前看的文章讲解的Java只有值传递