java对象引用出错_“Java有值传递和引用传递”为什么错了?

前言

初学Java的时候,老师在课堂上说“Java有值传递和引用传递”,但网上“Java只有值传递”的呼声很高。

本人在查找资料的过程中,在这两个说法之间反复横跳。经过本人的整理后,其实还真的是Java只有值传递。

什么是值传递?什么是引用传递?

首先,我们先明确一下值传递和引用传递的定义(来自维基百科)。

值传递

When a parameter is passed by value, the caller and callee have two independent variables with the same value. If the callee modifies the parameter variable, the effect is not visible to the caller.

拙译:当一个参数进行值传递的时候,调用者和被调用者有两个值相同的独立变量。如果被调用者修改了参数值,并不会影响调用者。

引用传递

When a parameter is passed by reference, the caller and the callee use the same variable for the parameter. If the callee modifies the parameter variable, the effect is visible to the caller’s variable.

拙译:当一个参数进行引用传递的时候,调用者和被调用者使用同一个变量。如果被调用者修改了参数值,调用者也会受到影响。

总结一下,值传递和引用传递最本质的区别在与,调用者和被调用者有没有使用同一个变量。

可能有人还是对这两个定义抱有疑问,没关系,我们用C++做个例子。

C++中的值传递和引用传递(不感兴趣可略过)

这里先说明下为什么用C++。因为C++的有指针概念,所以对于指针和引用是有做严格区分的,感兴趣的小伙伴可以看下这篇博客:C/C++中的值传递,引用传递,指针传递,指针引用传递。

基于需要,本文对C++的值传递和引用传递来进行简要说明。

C++的值传递

95e325e6f0f61a323656d39523ad49f2.png

可以看到,a变量(地址为0x22fe4c)在调用f()函数,进行值传递的变量p(地址为0x22fe20)已经是另一个变量了,而且在改变p的值后,地址为0x22fe4c的内容并没有改变,即a的值没有改变。所以值传递无法改变传递的变量的值。

引用传递

17665fecdce44091c5b97a16df5ff520.png

可以看到,a变量(地址为0x22fe4c)在调用f()函数,进行值传递的变量p(地址为0x22fe4c)还是同一个变量,而且在改变p的值后,地址为0x22fe4c的内容变为0xff,即a的值发生改变。所以引用传递可以改变传递的变量的值。

总结一下,在C++中的值传递和引用传递在大体上是跟值传递和引用传递的定义相符的。也就是说这个定义是可以在编程语言中适用的。

Java中的值传递和引用传递

说明一下,System.identityHashCode()的作用是用来判断两个对象是否是内存中同一个对象,跟用==判断内存地址是否一样的效果一样,有疑惑的朋友可以看下这篇博客:Java:Object.hashCode()和System.identityHashCode()的区别

值传递

fb518eff2ee554aa02e180e624f2aec1.png

以上可以得到跟值传递定义一样的结论,Java的值传递过程中,会复制传递的参数值到另一个变量,这两个变量之间互不影响,而且只有基本数据类型进行传递时是以值传递的方式。

引用传递

5a7168f580e409644e8fa9032f0cfa9b.png

从这个图也可以看出,Java引用传递过程中的两个数组a, b是指向同一个内存地址,这一个变量的改变会影响到另一个变量,而且只有除了String类型以外的其他对象类型在作为参数传递时,是使用引用传递的。

从这两个例子来看,Java既有值传递也有引用传递啊,所以“Java只有值传递”这个说法是错误的?非也。

String类型?

在查找资料的过程中,很多人的说法是“String类型也是值传递”,为什么呢?举个栗子:

public class Test {

public static void main(String[] args){

Test t = new Test();

String s = "oh";

t.test(s);

System.out.println("print in main , ans is " + s);

}

public void test(String s) {

s = "hello";

System.out.println("print in test , ans is " + s);

}

}

运行的结果是这样的:

print in test , ans is hello

print in main , ans is oh

我们可以看到实参s在传入方法test()后,形参s’改变了也不影响实参的值。为什么?(以下解释基于《深入理解Java虚拟机》的理解)

在字符串s传递过程中:

虚拟机在常量池划出一块内存,其地址为addr1,存值“oh”;

虚拟机在栈中分配s一块内存,内存中存的值为addr1;

虚拟机将s复制一份出来,即s‘,s和s’内存不同,但是它们的值都是addr1;

将s’传入方法体;

方法体在常量池中划分一块内存,其地址为addr2,存值"hello";

方法体将s’值改为addr2;

方法结束,main打印的是s,因为s存的值为addr1,所以打印出来的结果为addr1存的值:“oh”

所以String类型在调用过程中也是采用值传递。

“Java只有值传递”是错误的吗?

不是,我们拿“Java引用传递”的例子来解释。

public static void main(String[] args) {

int[] a = {1, 2, 3};

f(a);

...

}

public static void f(int[] b) {

b[0] = 100;

...

}

我们在得到结论的时候,是因为:在经过方法f()之后,a[0]的值从1改变为100。可这个过程严格上是引用传递吗?我们从虚拟机内部来观察传递过程:

虚拟机在堆上划分了一块用于存储数组a的内存,其内存地址为addr1。

虚拟机在栈中分配给a一个内存地址,这个地址存的是addr1。

虚拟机复制a,其别名为b,a和b的内存地址不同,但是他们的值都是addr1。

将b传入方法,方法改变了addr1的数组的值。

方法结束,f和main打印的都是addr1的内存值,都是同一个对象。

在这个过程中,a和b的内存地址不同,但是他们存值的内存地址后的对象是同一个。就是下图的这种关系:

11a7ef6999498d194486a8cd21baf26a.png

所以,从虚拟机的角度来看,实参a和形参b是两个独立变量,只是实参a把对象地址当做值传递给形参b。按照值传递的定义来看,a和b只是两个值相同的独立变量,Java是值传递。而a和b的值的值(这里不是写错)所指向的内容是同一个,所以我们前面在看到是“引用传递”的情况。

总结

严格来说,Java只有值传递,因为在实参传递的过程中,虚拟机复制了实参的值到形参,并且实参和形参指向的不是同一块内存。这个说法,是基于这种逻辑(a、b、c这三个变量是独立的,b为对象地址):

c72163e2ca6fbcdb79b748c69198eee4.png

这时,我们只要保证实参和形参是两个独立的个体,且值都是b就好。

而"Java有值传递和引用传递"这一说法的出现,是因为我们在刚学习Java的时候还不到了解虚拟机的水平,没有了解到,实参和形参是两个值相同的不同独立体,利用这种“美好的”误会来理解“引用传递”吧。

参考资料:

以及正文中提及的文章

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/291326.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

试图加载格式不正确的程序

部署IIS的时候,会出现 System.BadImageFormatException: Could not load file or assembly xxx or one of its dependencies. 试图加载格式不正确的程序。 问题分析: 由于编译中有的是32位有的是64位 不一致 解决办法: 1.找到IIS该站点对应的…

LeetCode之Missing Number

1、题目 Given an array containing n distinct numbers taken from 0, 1, 2, ..., n, find the one that is missing from the array. For example, Given nums [0, 1, 3] return 2. Given nums [0]return 12、代码实现 public class Solution {public int missingNumber(in…

Windows下卸载软件时提示 等待先前的卸载完成? 终止 dllhost.exe 进程

只要结束进程中的 “dllhost" 进程就好了。 估计原因是, 当卸载某些 "所谓的"较大型的软件的时候, 要去更新, 更改系统对dll链接库的注册, 更新。 通常这个过程等待的时间较长, 或者需要重新启动电脑才…

程序员生存之道-教你如何在丛林中捕获食物

文章目录 💥 序言🐣 躺🥝 从零到一还是从零到100?🍄 螺丝钉文化🍔 价值分析🍓 长期主义者?🥬 何为顺其自然?🌈 总结 💥 序言 嗨&#…

微软发文庆祝 .NET 诞生 20 周年纪念日!

技术编辑:MissD丨发自 思否编辑部公众号:SegmentFault刚刚过去的“情人节”里,.NET 团队为庆祝 .NET 社区诞生 20 周年而举办了一场盛大的活动。没错!.NET 于 2002 年 2 月 13 日与 Visual-Studio 一起推出,本月终于迎…

java代码实现四舍五入_Java中四舍五入实现方法

Java中四舍五入实现方法Java有四舍五入函数–Math.round,通过一个例子看看他的用法:[java] view plaincopypackage math;public class MathRoundTest {/*** Math类中提供了三个与取整有关的方法:ceil,floor,round,* 这些方法的作用于它们的英…

【动态规划】[Uva11270]Tiling Dominoes

这道题就是连通性状态压缩DP&#xff0c;复习了一下。 #include <cstdio> #include <iostream> #include <cstring> #include <map> using namespace std; long long dp[11][11][(1<<11)1][2], n, m; long long dfs(int x, int y, int md, int r…

LeetCode之Find All Numbers Disappeared in an Array

1、题目 Given an array of integers where 1 ≤ a[i] ≤ n (n size of array), some elements appear twice and others appear once. Find all the elements of [1, n] inclusive that do not appear in this array. Could you do it without extra space and in O(n) runti…

根据文件扩展名得到文件对应该类型Icon方法

2019独角兽企业重金招聘Python工程师标准>>> 根据文件扩展名得到文件对应该类型Icon方法 package com.fleety.util; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.GraphicsConfiguration; import java.awt.GraphicsDevice; import java.…

Asp-Net-Core开发笔记:在docker部署时遇到一个小坑

哦吼之前刚说了尝试了使用docker来部署AspNetCore应用&#xff08;Asp.Net Core部署&#xff1a;早知道&#xff0c;还是docker!以及一点碎碎念&#xff09;&#xff0c;结果这才刚上班就遇到问题了 …我这项目用的数据库是Oracle&#xff0c;之前直接运行没啥问题&#xff0c;…

lasso特征选择python_转:结合Scikit-learn介绍几种常用的特征选择方法-2

4.2 平均精确率减少 Mean decrease accuracy另一种常用的特征选择方法就是直接度量每个特征对模型精确率的影响。主要思路是打乱每个特征的特征值顺序&#xff0c;并且度量顺序变动对模型的精确率的影响。很明显&#xff0c;对于不重要的变量来说&#xff0c;打乱顺序对模型的精…

shell中条件判断if中的-z到-d的意思

shell中条件判断if中的-z到-d的意思 [ -a FILE ] 如果 FILE 存在则为真。 [ -b FILE ] 如果 FILE 存在且是一个块特殊文件则为真。 [ -c FILE ] 如果 FILE 存在且是一个字特殊文件则为真。 [ -d FILE ] 如果 FILE 存在且是一个目录则为真。 [ -e FILE ] 如果 FILE 存在则为真。…

Mac Generating Pods project Abort trap: 6

为什么80%的码农都做不了架构师&#xff1f;>>> 为项目添加cocoapods如果产生此种错误时,主要有以下几点原因: 1,cocoapods版本过低: 打开终端在终端输入:pod --version,目前最新版本是1.2.0(2017年3月),如果发现版本过低,则可以在终端输入以下命令:gem install co…

svn 服务器搭建

2019独角兽企业重金招聘Python工程师标准>>> Svn搭建 1. Linux 搭建 YUM 服务器 [rootlocalhost conf]# yum install -y subversion 2.验证安装版本&#xff1a; [rootlocalhost conf]# svnserve –version 3.创建SVN 版本库 [rootlocalhost conf]# mkdir /v…

Android之提示can‘t execute: Permission denied解决办法

1、问题 在手机里面执行文件的时候提示 cant execute: Permission denied 一开始以为是没有root权限&#xff0c;自己傻逼了&#xff0c;错误意思是&#xff0c;不能执行&#xff0c;权限定义&#xff0c; 2、解决办法 chmod 777 file 给文件可执行就可以。 一般把文件放到…

C# 使用 ValueTasks

C# 7 带有更灵活的 await 关键字&#xff1b;它现在可以等待任何提供 GetAwaiter 方法的对象。一种可用于等待的新类型是 ValueTask。与 Task 类相反&#xff0c;ValueTask 是一个结构。这具有性能优势&#xff0c;因为 ValueTask 在堆上没有对象。与异步方法调用相比&#xff…

LeetCode之Reverse String II

1、题目 Given a string and an integer k, you need to reverse the first k characters for every 2k characters counting from the start of the string. If there are less than k characters left, reverse all of them. If there are less than 2k but greater than or …

Electron - 创建跨平台的桌面客户的应用程序

Electron 框架的前身是 Atom Shell&#xff0c;可以让你写使用 JavaScript&#xff0c;HTML 和 CSS 构建跨平台的桌面应用程序。它是基于io.js 和 Chromium 开源项目&#xff0c;并用于在 Atom 编辑器中。Electron 是开源的&#xff0c;由 GitHub 维护&#xff0c;有一个活跃的…

Cognos TM1_10.1.1服务端配置

场景&#xff1a;本文继Cognos TM1_10.1.1服务端安装 之后&#xff0c;简单的说一下本人对简单配置的拙见,确保服务端在安装过程一切正常&#xff0c;成功安装。 1&#xff1a;进入TM的Cognos Configuration 2&#xff1a;如下图&#xff0c;选中环境&#xff0c;这里可以看出…

java黄油刀_一篇文章玩转ButterKnife,让代码更简洁

前言话说&#xff0c;Android开发的兄弟们都知道&#xff0c;每次初始化控件&#xff0c;设置相应的事件&#xff0c;写的那点过程多而且恶心。我们先一块回顾下不堪的曾经~那些年&#xff0c;我们是这样初始化控件&#xff1a;// 每次的习惯上来写一个initView()方法tvContent…