关于值传递和引用传递

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,但是此时他们已经没有关系了。

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

4430 次点击
所在节点    程序员
50 条回复
joshryo
2018-07-20 15:24:11 +08:00
@alamaya 楼主虽然问的是参数传递问题,但是这里值没有发生变化却是因为 Integer 是不可变类导致的。
ShineSmile
2018-07-20 15:27:59 +08:00
@sagaxu #1 证据?
luopengfei14
2018-07-20 16:07:53 +08:00
看来楼主 C 语言没学好
虽然我是对着答案。。。看懂代码的
luopengfei14
2018-07-20 16:13:24 +08:00
Integer i = new Integer(0);
...
private void add(Integer i)
...

虽然这两个引用(指针)指向同一个实例,但是只是两个指针,后来 add()中 i 的指向变了,也就不会改变原来指向的实例。

我的想法是这样。。。
johnj
2018-07-20 16:30:55 +08:00
首先 Java 里只有按值传递。所谓按值传递,遇到值类型,复制一份传进去;遇到引用类型,复制一份引用传进去。会出现多个引用指向同一个对象的情况:可以通过每个引用,来改变指向的对象的属性值;其中一个引用改变了指向的对象,对其他的引用没有影响。
johnj
2018-07-20 17:33:41 +08:00
@jzq526 Java 里只有按值传递(复制),没有按引用传递
unforgiven
2018-07-20 18:52:45 +08:00
@lux182 因为他是参数的内存地址的值得拷贝,所以在访问这个参数的时候指向的是同一个内存地址,当你修改这个参数 i 的时候,你修改的只是这个拷贝的值并不会影响到原来的值
unforgiven
2018-07-20 19:05:52 +08:00
@suixn 和可变不可变没有关系
@Test
public void patchModuleRecord() {
User user = new User("aaa","bbb");
testan(user);
System.out.print(user.toString());
test(user);
System.out.print(user.toString());
}

public void test(User user){
user = new User("123","456");
}

public void testan(User user){
user.userName = "ccc";
}

class User{
String userName;
String password;
public User(String userName, String password){
this.userName = userName;
this.password = password;
}

public User(){

}

@Override
public String toString() {
return "User{" +
"userName='" + userName + '\'' +
", password='" + password + '\'' +
'}';
}
}
User{userName='ccc', password='bbb'}User{userName='ccc', password='bbb'}
我的 User 对象到是可变的,一样你在函数里修改不了 user 的引用,说到底还是 Java 的所谓引用
还是传递的内存地址的值,真不了解的话拿着 c 语言的指针去玩玩你就懂了
jzq526
2018-07-21 06:45:36 +08:00
@johnj 我想你没有仔细看。我说的是 C 和 Java 的参数传递其实是一样的,形式上都是将实参的值复制给形参。所谓值传递或者地址传递或者引用传递,只是复制的内容不同罢了。Java 中的引用,实际上就是一个受限制的指针。当你用数组做参数或者对象做参数时,复制过去的就是这个数组、对象的地址,也就是指针,或者 Java 中叫做引用。你可以写个方法,参数是一个对象,改变参数中一个属性,再调用这个方法,你看看对象中的属性会不会改变。
johnj
2018-08-07 11:19:34 +08:00
@jzq526 嗯 如果不看叫法( pass by value / pass by reference )咱俩说的应该是一个意思

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

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

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

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

© 2021 V2EX