一道笔试题来理顺Java中的值传递和引用传递

前段时间参加了一场面试,其中有一道引用传递的题,因为当时并没有考虑清楚所以做错了。 现在来复盘一下,题目如下:

private static void change(StringBuffer str11, StringBuffer str12) {

    str12 = str11;
    str11 = new StringBuffer("new world");
    str12.append("new world");
}

public static void main(String[] args) {
    StringBuffer str1 = new StringBuffer("good ");
    StringBuffer str2 = new StringBuffer("bad ");
    change(str1, str2);
    System.out.println(str1.toString());
    System.out.println(str2.toString());
}

就是这么一个很简单的题目,大家应该知道答案吧? 答案是: good new world bad

下面就来复盘一下为何是这两个答案,下面会做详细的图文分析。 要搞明白这个问题,那么就需要明白Java中的值传递和引用传递了。

Java对象参数传递虽然传递的是地址(引用),但仍然是值调用。是时候需要给引用调用和值调用一个准确的定义了。

  • 值调用(call by value): 在参数传递过程中,形参和实参占用了两个完全不同的内存空间。形参所存储的内容是实参存储内容的一份拷贝。实际上,Java对象的传递就符合这个定义,只不过形参和实参所储存的内容并不是常规意义上的变量值,而是变量的地址。咳,回过头想想:变量的地址不也是一种值吗!
  • 引用调用(call by reference) : 在参数传递的过程中,形参和实参完全是同一块内存空间,两者不分彼此。 实际上,形参名和实参名只是编程中的不同符号,在程序运行过程中,内存中存储的空间才是最重要的。不同的变量名并不能说明占用的内存存储空间不同。

那么接下来就画图分析文章开头抛出的问题: 这里再把代码贴一遍,大家可以对比着代码和图一起看。

private static void change(StringBuffer str11, StringBuffer str12) {

    str12 = str11;//2
    str11 = new StringBuffer("new world");//3
    str12.append("new world");//4
}//5

public static void main(String[] args) {
    StringBuffer str1 = new StringBuffer("good ");
    StringBuffer str2 = new StringBuffer("bad ");
    change(str1, str2);//1
    System.out.println(str1.toString());
    System.out.println(str2.toString());
}
  • 执行到第一步的图:
  • 执行到第二步的图,这里chage方法中的形参str11,str22是实参str1,str2的地址拷贝。
  • 这里str11和str22是实参str1,str2的地址拷贝,接着执行第三步的图:
  • 上图中str12地址指向了“good”,接着看第四步图:
  • 上图中str11的地址指向了堆中新的对象"new world",接着看第五步的图:

这里图算是画完了,确实不太擅长画图,大家就先勉强看吧。 这里有一个很重要的点就是StringBuffer是可变的,具体解释大家可以搜索下。

因为这里str12.append("new world")直接修改了good的值。所以main函数中打印的会是:good new world。

到这里就算讲完了,一个很简单的例子,这里算是对之前一道笔试题的复盘,有不对的地方欢迎大家指正。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏机器学习入门

LWC 61:740. Delete and Earn

LWC 61:740. Delete and Earn 传送门:740. Delete and Earn Problem: Given an array nu...

2145
来自专栏DHUtoBUAA

《剑指Offer》附加题_用两个队列实现一个栈_C++版

  在《剑指Offer》中,在栈和队列习题中,作者留下来一道题目供读者自己实现,即“用两个队列实现一个栈”。   在计算机数据结构中,栈的特点是后进先出,即最后...

2965
来自专栏魂祭心

原 Promise 实现

2885
来自专栏机器学习从入门到成神

javascript数组去重方法

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/sinat_35512245/articl...

871
来自专栏Fundebug

10个JavaScript常见BUG及修复方法

1305
来自专栏小勇DW3

concrrent类下 BlockingDeque 下 自己实现代码编写

  java6增加了两种容器类型,Deque和BlockingDeque,它们分别对Queue和BlockingQueue进行了扩展。 Deque是一个双端队...

782
来自专栏写代码的海盗

scala如何解决类型强转问题

scala如何解决类型强转问题   scala属于强类型语言,在指定变量类型时必须确定数据类型,即便scala拥有引以为傲的隐式推到,这某些场合也有些有心无力。...

3459
来自专栏屈定‘s Blog

(转)Java--栈与队列

Java中栈与队列相比集合来说不是很常用的数据结构,因此经常被忽略.个人觉得还是有必要掌握下,以备不时之需. Java中实际上提供了java.util.Stac...

4563
来自专栏不想当开发的产品不是好测试

java array to list

背景 想把数组转为list,使用list的判断元素是否存在的方法,结果发现一个坑,int类型的数组失败了  步骤 public static void main...

1995
来自专栏Java技术栈

你知道void和Void的区别吗?

区别 void 用于无返回值的方法定义。 Void Void是void的包装方法,和其他基础类型的包装方法不同是Void不能被实例化,Void还可用于一直返回n...

34611

扫码关注云+社区

领取腾讯云代金券