关于值传递和引用传递

2018-07-20 00:29:43 +08:00
 lux182

今天看到一面试题,对于输出结果为 0 有很多一知半解的人解释,

对于新手来说看得似懂非懂,然后看完还是一头雾水。

 @Test
    public void test1(){
        Integer i = new Integer(0);
        //Integer@853 -----1
        add(i);
        //Integer@853 -----5
        System.out.println(i);//0
        i +=3;
        //Integer@864 -----6
        System.out.println(i);//3
    }
    private void add(Integer i) {
        //Integer@853 -----2
        i = i + 3;
        //Integer@864 -----3
        i = new Integer(i);//3
        //Integer@865 -----4
    }    

在代码上我都标注了 i 的各步骤的引用地址。

从调试信息上来看,方法传递的就是对象的地址。

而让新手迷惑的关键地方是,add 方法中改变了 i 的值啊,为什么还是返回 0 ?

Integer 的加法运算生成了一个新的 Integer 对象,并申明为变量 i,而局部变量的生命周期只存在自己的方法中,两个方法中的变量名都为 i,但是此时他们已经没有关系了。

不知道解释的是否正确,希望错误的地方各位指正,以免让别人产生误解。

4586 次点击
所在节点    程序员
50 条回复
sagaxu
2018-07-20 00:40:12 +08:00
这是 Java 吗? Java 方法调用是没有传引用的,都是传值。
zjp
2018-07-20 00:47:49 +08:00
都打印出 Integer@853 只是说明 test1()中的 i 和 add()中的 i 指向同一个地址,后者是前者的副本,依然为传值。我觉得不要引入"传引用"这个概念更清晰
lhx2008
2018-07-20 00:51:44 +08:00
很简单,传进函数里面的会复制一份引用,就是 int b = a,你再改 b=1,因为 a 本身不可变,所以没用,a 不会变,但是如果 a 是可变的对象,再引用是可以改里面的内容的
lhx2008
2018-07-20 00:54:15 +08:00
a 不可变的话,改 b 等于是重新赋值,a 可变,那可以改 a 里面的内容不用重新赋值
lhx2008
2018-07-20 00:58:23 +08:00
关键在于你有没对函数的参数变量重新赋值,没有的话是可以改对象内容的
tsaohai
2018-07-20 01:41:23 +08:00
test1 里边的 i 在 add 调用前后不会改变,add 里边那个 i 的 copy 变了。
starcraft
2018-07-20 07:31:21 +08:00
这是书看少了啊。有点名气的外国佬的书,都提到了 java 值传递特性吧。
araraloren
2018-07-20 08:22:26 +08:00
C 系列语言的参数传递不都是传值的么。。
unforgiven
2018-07-20 09:09:07 +08:00
@araraloren c++有引用传递
unforgiven
2018-07-20 09:13:24 +08:00
你的 add 方法,实际传入的 i 其实是参数的内存地址的值得拷贝
araraloren
2018-07-20 09:14:29 +08:00
@unforgiven :) 莫名就忽略了它。。。
lux182
2018-07-20 09:15:20 +08:00
@unforgiven 怎么解释指向相同的地址呢
hugedata
2018-07-20 09:23:40 +08:00
@sagaxu 这是 c#。
saberpowermo
2018-07-20 09:25:10 +08:00
java 的‘’引用‘’传递 传递的是这个引用的 内存地址值
hugedata
2018-07-20 09:26:41 +08:00
@sagaxu 收回我的话。sorry
raysonx
2018-07-20 09:33:14 +08:00
@hugedata C#我了解一些,参数前加 ref 或者 out 关键字才是传引用,否则都是传值。传引用比如:
void swap(ref int a, ref int b) {
int t = a;
a = b;
b = t;
}

调用时,两个变量的赋值会被调换:
int x = 3;
int y = 5;
swap(ref x, ref y);
Jarvix
2018-07-20 09:33:53 +08:00
这是 java 吗?
public static void main(String[] args) {

Integer i = new Integer(0);
i = add(i);
System.out.println(i);//0
}
private static Integer add(Integer i) {
i = i + 3;
i = new Integer(i);//3
return i;
}
}
好像这样才行。(我也是上周才开始学的 java )逃……
WhyAreYouSoSad
2018-07-20 09:34:24 +08:00
赞成楼上 2,3 楼说的,其实不要用引用传递这种说法,因为还是值传递的操作影响。我读书时有想过这个问题。
其实在面向对象里面,所谓的引用传递他是传递一个一级指针(不能改变该对象指向的空间,只能改变该对象成员的值),而所谓的值传递,就是传值。
如果用 c 语言表达的话就是,如果类要改变指向,那么就必须传一个二级指针过去。而基础类型要改变指向就传一个一级指针,要把一个类当成一个不定长的一维数组。
raysonx
2018-07-20 09:36:36 +08:00
楼主的例子中,add 函数的参数前没加 ref,当然是传值。后续对 i 重新赋值不会影响调用方的变量的值。
另外,这里 i 的类型用 int 还是 Integer 效果上没有什么区别,不知道楼主为何要用 Integer。
alamaya
2018-07-20 09:37:42 +08:00
可以这么理解 java 值传递传的是指针地址的 copy

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/472515

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX