为什么说java只有按值传递

为什么说java只有按值传递

学过Java基础的人都知道:值传递和引用传递是初次接触Java时的一个难点,有时候记得了语法却记不得怎么实际运用,有时候会的了运用却解释不出原理,而且坊间讨论的话题又是充满争议:有的论坛帖子说Java只有值传递,有的博客说两者皆有;这让人有点摸不着头脑。

Java到底是按值传递还是按引用传递的呢?国外的网站上关于这个问题的讨论非常之多。官方答案:The Java Spec says that everything in Java is pass-by-value. There is no such thing as “pass-by-reference” in Java. 官方的说法是在java中只有按值传递,并没有所谓的按引用传递。

错误理解

在开始深入讲解之前,有必要纠正一下大家以前的那些错误看法了。如果你有以下想法,那么你有必要好好阅读本文。

错误理解一:值传递和引用传递,区分的条件是传递的内容,如果是个值,就是值传递。如果是个引用,就是引用传递。

错误理解二:传递的参数如果是普通类型,那就是值传递,如果是对象,那就是引用传递。

实参与形参

形式参数:是在定义函数名和函数体的时候使用的参数,目的是用来接收调用该函数时传入的参数。

实际参数:在调用有参函数时,主调函数和被调函数之间有数据传递关系。在主调函数中调用一个函数时,函数名后面括号中的参数称为“实际参数”。

求职策略

我们说当进行方法调用的时候,需要把实际参数传递给形式参数,那么传递的过程中到底传递的是什么东西呢?

这其实是程序设计中**求值策略(Evaluation strategies)**的概念。

在计算机科学中,求值策略是确定编程语言中表达式的求值的一组(通常确定性的)规则。求值策略定义何时和以何种顺序求值给函数的实际参数、什么时候把它们代换入函数、和代换以何种形式发生。

值传递和引用传递

定义

  1. 值传递:是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。
  2. 引用传递:是指在调用函数时将实际参数的地址直接传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数。

例子

有了上面的概念,然后大家就可以写代码实践了

于是就有了下面的代码:

public static void main(String[] args) {
   Test t = new Test();
   int i = 10;
   t.pass(i);
   System.out.println("主方法:"+i);
}

public void m(int j) {
   j = 20;
   System.out.println("m方法:"+j);
}

上面的代码中,我们在m方法中修改了参数j的值,然后分别在m方法和main方法中打印参数的值。输出结果如下:

m方法:20
主方法:10

可见,m方法内部对j的值的修改并没有改变实际参数i的值。那么,按照上面的定义,有人得到结论:Java的方法传递是值传递。

但是,很快就有人提出质疑了。然后,他们会搬出以下代码:

public static void main(String[] args) {
    Test t = new Test();

    User u = new User();
    u.setName("张三");
    t.m(u);
    System.out.println("主方法:" + u.getName());
}

public void m(User user) {
    user.setName("李四");
    System.out.println("m方法:" + user.getName());
}

运行结果为:

m方法:李四
主方法:李四

看到这两个例子,很多人可能就理解为:传递的参数如果是普通类型,那就是值传递,如果是对象,那就是引用传递。

如果你这样理解,那么你就进入了一个思维误区。我们再看一下下面这个例子:

public static void main(String[] args) {
    Test t = new Test();

    String name = "张三";
    t.m(name);
    System.out.println("主方法:" + name);
}

public void m(String name) {
    name = "李四";
    System.out.println("m方法:" + name);
}

运行结果为:

m方法:李四
主方法:张三

这又作何解释呢?同样传递了一个对象,但是原始参数的值并没有被修改,难道传递对象又变成值传递了?

上面,我们举了三个例子,表现的结果却不一样,这也是导致很多初学者对于Java的传递类型有困惑的原因。

分析

其实,我们上面的例子中,第二个例子是存在一定问题的。

我在寻找这个问题答案的时候曾看到过一个例子,我觉得很贴切,这里我就借用一下

你有一把钥匙,当你的朋友想要去你家的时候,如果你直接把你的钥匙给他了,这就是引用传递。这种情况下,如果他对这把钥匙做了什么事情,比如他在钥匙上刻下了自己名字,那么这把钥匙还给你的时候,你自己的钥匙上也会多出他刻的名字。

你有一把钥匙,当你的朋友想要去你家的时候,你复刻了一把新钥匙给他,自己的还在自己手里,这就是值传递。这种情况下,他对这把钥匙做什么都不会影响你手里的这把钥匙。

但是,不管上面哪种情况,你的朋友拿着你给他的钥匙,进到你的家里,把你家的电视砸了。那你说你会不会受到影响?

而我们在m方法中,改变user对象的name属性的值的时候,不就是在“砸电视”么。你改变的不是那把钥匙,而是钥匙打开的房子。

所以说,第二个例子是存在一定问题的。

我们再来看下面这个例子:

public static void main(String[] args) {
    Test t = new Test();

    User u = new User();
    u.setName("张三");
    System.out.println("更改前:" + u);
    t.m(u);
    System.out.println("更改后:" + u);
}

public void m(User user) {
    user.setName("李四");
    //System.out.println("m方法:" + user.getName());
}

运行结果为:

更改前:utils.User@15db9742
更改后:utils.User@15db9742

从结果可以看出,我们传递的对象的地址是没有被改变的,说明我们的钥匙没有被更改,更改的只是钥匙对应的对象。

在判断实参内容有没有受影响的时候,要看传的的是什么,如果你传递的是个地址,那么就看这个地址的变化会不会有影响,而不是看地址指向的对象的变化。就像钥匙和房子的关系。所以说,Java中其实还是值传递的,只不过对于对象参数,值的内容是对象的引用。

其实,我们有一个简单的辨别方法,那就是我们定义里说的:

值传递是会将实际参数复制一份,而引用传递是没有复制的,所以,值传递和引用传递的区别并不是传递的内容。而是实参到底有没有被复制一份给形参。

结论

java只有按值传递

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值