网站首页 > 技术文章 正文
许多编程语言都允许按引用或按值传递参数。在Java中,我们只能按value传递参数。这施加了一些限制,并且引起了疑问。例如,如果在方法中更改了参数值,方法执行后该值会怎样?您可能还想知道Java如何管理内存堆中的对象值。该Java Challenger可帮助您解决有关Java中对象引用的这些以及其他常见问题。
获取源代码
在遵循示例的同时,您可以运行自己的测试。源码地址: https://github.com/rafadelnero/javaworld-challengers
对象引用按值传递
Java中的所有对象引用均按值传递。这意味着该值的副本将传递给方法。但是诀窍在于传递值的副本也会更改对象的实际值。要了解原因,请从以下示例开始:
public class ObjectReferenceExample {
public static void main(String... doYourBest) { Simpson simpson = new Simpson(); transformIntoHomer(simpson); System.out.println(simpson.name); }
static void transformIntoHomer(Simpson simpson) { simpson.name = "Homer"; }}class Simpson { String name;}
您认为执行simpson.name该transformIntoHomer方法后会怎样?
在这种情况下,它将是荷马!原因是Java对象变量只是指向内存堆中实际对象的引用。因此,即使Java通过值将参数传递给方法,但是如果变量指向对象引用,则实际对象也将被更改。
如果您仍然不太清楚它是如何工作的,请看下图。
拉斐尔·奇内拉托·德尔尼罗
原始类型是否按值传递?
像对象类型一样,基本类型也按值传递。您能否在下面的代码示例中推断出原始类型会发生什么?
public class PrimitiveByValueExample {
public static void main(String... primitiveByValue) { int homerAge = 30; changeHomerAge(homerAge); System.out.println(homerAge); }
static void changeHomerAge(int homerAge) { homerAge = 35; }}
如果您确定该值将更改为30,那么您是正确的。这是30,因为(再次)Java通过值传递对象参数。数字30只是值的副本,而不是实际值。基本类型在堆栈存储器中分配,因此仅本地值将被更改。在这种情况下,没有对象引用。
传递不可变的对象引用
如果我们对不可变的String对象进行了相同的测试该怎么办?
JDK包含许多不可变的类。实例包括包装类型Integer,Double,Float,Long,Boolean,BigDecimal,和当然的非常公知的String类。
在下一个示例中,请注意当我们更改a的值时会发生什么String。
public class StringValueChange {
public static void main(String... doYourBest) { String name = ""; changeToHomer(name); System.out.println(name); }
static void changeToHomer(String name) { name = "Homer"; }}
您认为输出结果是什么?如果您猜到了“”,那就恭喜!发生这种情况是因为String对象是不可变的,这意味着内的字段String是最终字段,无法更改。
使String类不可变使我们可以更好地控制Java最常用的对象之一。如果a的值String可以更改,则会产生很多错误。还要注意,我们没有更改String类的属性;相反,我们只是String为其分配了一个新值。在这种情况下,“荷马”值将被传递到name在changeToHomer方法。该方法完成执行后,String“本垒打”将有资格被垃圾回收changeToHomer。即使无法更改对象,局部变量也会更改。
传递可变对象引用
与不同String,JDK中的大多数对象都是可变的,例如StringBuilder类。下面的示例与上一个示例相似,但是功能StringBuilder而不是String:
static class MutableObjectReference { public static void main(String... mutableObjectExample) { StringBuilder name = new StringBuilder("Homer "); addSureName(name); System.out.println(name); }
static void addSureName(StringBuilder name) { name.append("Simpson"); } }
您能否推断出此示例的输出?在这种情况下,因为我们正在使用可变对象,所以输出将为“ Homer Simpson”。您可能期望Java中的任何其他可变对象具有相同的行为。
您已经了解到Java变量是通过值传递的,这意味着将传递值的副本。只要记住复制的值指向Java内存堆中的真实对象即可。按值传递仍然会更改实际对象的值。
接受对象引用挑战!
在此Java Challenger中,我们将测试您从对象引用中学到的知识。在下面的代码示例中,您将看到不可变String和可变的StringBuilder类。每个参数都作为参数传递给方法。知道Java仅按值传递,一旦执行了此类的main方法,您认为输出是什么?
public class DragonWarriorReferenceChallenger {
public static void main(String... doYourBest) { StringBuilder warriorProfession = new StringBuilder("Dragon "); String warriorWeapon = "Sword "; changeWarriorClass(warriorProfession, warriorWeapon);
System.out.println("Warrior=" + warriorProfession + " Weapon=" + warriorWeapon); }
static void changeWarriorClass(StringBuilder warriorProfession, String weapon) { warriorProfession.append("Knight"); weapon = "Dragon " + weapon;
weapon = null; warriorProfession = null; }}
这些是选项,请在本文结尾处查看答案。
A:战士=空武器=空B:战士=龙武器=龙C:战士=龙骑士武器=龙剑D:战士=龙骑士武器=剑
发生什么事了
上面示例中的第一个参数是warriorProfession变量,它是一个可变对象。第二个参数,武器是不可变的String:
static void changeWarriorClass(StringBuilder warriorProfession, String weapon) { ... }
现在,让我们分析一下该方法内部发生的情况。在此方法的第一行,我们将Knight值附加到warriorProfession变量。请记住,这warriorProfession是一个可变的对象;因此实际对象将被更改,并且其值将为“ Dragon Knight”。
warriorProfession.append("Knight");
在第二条指令中,不变的局部String变量将更改为“ Dragon Sword”。但是,由于实物String是不可变的并且其属性是最终的,因此实物永远不会更改:
weapon = "Dragon " + weapon;
最后,我们传递null给这里的变量,而不传递给对象。只要它们仍然可以从外部访问,这些对象将保持不变-在这种情况下,可以通过main方法访问。而且,尽管局部变量将为null,但对象将不会发生任何事情:
weapon = null;warriorProfession = null;
从所有这些我们可以得出结论,我们可变StringBuilder且不变的最终值String将是:
System.out.println("Warrior=" + warriorProfession + " Weapon=" + warriorWeapon);
changeWarriorClass方法中唯一更改的值是warriorProfession,因为它是可变StringBuilder对象。请注意,它warriorWeapon没有改变,因为它是一个不可变的String对象。
我们的挑战者代码的正确输出为:
D:战士=龙骑士武器=剑。
视频挑战!调试Java中的对象引用
调试是完全吸收编程概念并改善代码的最简单方法之一。在本视频中,您可以在调试和解释Java对象引用时进行后续操作。
视频地址:https://youtu.be/HXvKLn5RkRQ
对象引用的常见错误
- 尝试通过引用更改不可变值。
- 尝试通过引用更改原始变量。
- 当您在方法中更改可变对象参数时,期望真实对象不会更改。
关于对象引用要记住什么
- Java总是按值传递参数变量。
- Java中的对象变量始终指向内存堆中的实际对象。
- 可变对象的值传递给方法时可以更改。
- 不可变对象的值即使传递了新值也无法更改。
- “按值传递”是指传递值的副本。
- “通过引用传递”是指在内存中传递变量的真实引用。
猜你喜欢
- 2024-10-22 经典问题探讨:Java中为什么只有值传递?
- 2024-10-22 深入理解Java中方法的参数传递机制
- 2024-10-22 什么是按值传递和按引用传递,Python属于哪一种?
- 2024-10-22 为什么Java只有值传递(java为什么要有数据类型)
- 2024-10-22 【Java】中只有值传递,没有引用传递!
- 2024-10-22 阿瑟Java (19):Java 的函数是值传递吗?
- 2024-10-22 Java-值传递(java值传递机制)
- 2024-10-22 你们不要再吵了!Java只有值传递..
- 2024-10-22 Java中的值传递有什么作用?(java 值传递还是引用传递)
- 2024-10-22 java的值传递和引用传递(java 传值传引用区别)
- 1507℃桌面软件开发新体验!用 Blazor Hybrid 打造简洁高效的视频处理工具
- 505℃Dify工具使用全场景:dify-sandbox沙盒的原理(源码篇·第2期)
- 484℃MySQL service启动脚本浅析(r12笔记第59天)
- 465℃服务器异常重启,导致mysql启动失败,问题解决过程记录
- 462℃启用MySQL查询缓存(mysql8.0查询缓存)
- 442℃「赵强老师」MySQL的闪回(赵强iso是哪个大学毕业的)
- 422℃mysql服务怎么启动和关闭?(mysql服务怎么启动和关闭)
- 418℃MySQL server PID file could not be found!失败
- 最近发表
-
- netty系列之:搭建HTTP上传文件服务器
- 让deepseek教我将deepseek接入word
- 前端大文件分片上传断点续传(前端大文件分片上传断点续传怎么操作)
- POST 为什么会发送两次请求?(post+为什么会发送两次请求?怎么回答)
- Jmeter之HTTP请求与响应(jmeter运行http请求没反应)
- WAF-Bypass之SQL注入绕过思路总结
- 用户疯狂点击上传按钮,如何确保只有一个上传任务在执行?
- 二 计算机网络 前端学习 物理层 链路层 网络层 传输层 应用层 HTTP
- HTTP请求的完全过程(http请求的基本过程)
- dart系列之:浏览器中的舞者,用dart发送HTTP请求
- 标签列表
-
- c++中::是什么意思 (83)
- 标签用于 (65)
- 主键只能有一个吗 (66)
- c#console.writeline不显示 (75)
- pythoncase语句 (81)
- es6includes (73)
- windowsscripthost (67)
- apt-getinstall-y (86)
- node_modules怎么生成 (76)
- chromepost (65)
- c++int转char (75)
- static函数和普通函数 (76)
- el-date-picker开始日期早于结束日期 (70)
- js判断是否是json字符串 (67)
- checkout-b (67)
- localstorage.removeitem (74)
- vector线程安全吗 (70)
- & (66)
- java (73)
- js数组插入 (83)
- linux删除一个文件夹 (65)
- mac安装java (72)
- eacces (67)
- 查看mysql是否启动 (70)
- 无效的列索引 (74)