Java到底是不是值传递

版权声明:本文为博主原创文章,转载请注明出处 https://blog.csdn.net/u011109881/article/details/80458946

最近在研究排序算法时遇到一个问题:

int B[] = { 9, 8, 7, 6, 5, 4, 3, 2, 1 };// 从小到大希尔排序
        int n = B.length;
        ShellSort(B, n);//对数组进行排序
        getArr(B);//输出数组内容

我记得Java是值传递的,但此处为什么实参B进入方法ShellSort之后,操作完B的“内容”却被修改了呢?

我在网上搜索了一下,发现了一篇文章,文章的例子讲的很好,但是解释的太复杂。我借用一下文中的例子和一些定义。
原文链接:程序员小灰
https://mp.weixin.qq.com/s/OfkwrsoE6OPagWiFlaGcVg

形参和实参

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

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

简单举个例子:

public static void main(String[] args) {
   ParamTest pt = new ParamTest();
   pt.sout("Hollis");//实际参数为 Hollis
}

public void sout(String name) { //形式参数为 name
   System.out.println(name);
}

实际参数是调用有参方法的时候真正传递的内容,而形式参数是用于接收实参内容的参数。

值传递和引用传递的定义

值传递(pass by value)是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。

引用传递(pass by reference)是指在调用函数时将实际参数的地址直接传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数。

有了定义,我们再从实际例子中看下为什么说Java只有值传递

三个简单案例和解析

public class PassByValue {

    private void pass1(int i) {
        i = 20;
        System.out.println("i in pass1 is " + i);
    }

    private void pass2(User u) {
        u.setName("Jerry");
        System.out.println("user name in pass2 is " + u.getName());
    }

    private void pass3(String str) {
        str = "World";
        System.out.println("String in pass3 is " + str);
    }

    public static void main(String[] args) {
        PassByValue pass = new PassByValue();

        //案例1
        int x = 10;
        System.out.println("x in main is " + x);
        pass.pass1(x);
        System.out.println("x in main is " + x);

        //案例2
        User user = new User("Tom");
        System.out.println("user name in main is " + user.getName());
        pass.pass2(user);
        System.out.println("user name in main is " + user.getName());

        //案例3
        String s = new String("Hello");
        System.out.println("string in main is " + s);
        pass.pass3(s);
        System.out.println("string in main is " + s);
    }

    public static class User {
        String name;

        User(String name) {
            this.name = name;
        }

        private void setName(String name) {
            this.name = name;
        }

        private String getName() {
            return name;
        }
    }
}

我分别在传入方法之前 方法中 方法执行之后打印参数的值,结果如下:

x in main is 10
i in pass1 is 20
x in main is 10
user name in main is Tom
user name in pass2 is Jerry
user name in main is Jerry
string in main is Hello
String in pass3 is World
string in main is Hello

在解释之前我们再回顾一下值传递的定义:
值传递(pass by value)是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。
案例一分析
这是经典的值传递的案例,在main中的x的值被拷贝给形参,形参修改了值,但是不会影响原来的值。这就像原来有个遥控器,现在复制了一个遥控器,我们在这个复制的遥控器上写名字,画画,甚至把它砸成两半都不会影响原来的遥控器。
案例二分析
这个案例是导致很多Java有引用传递说法产生的根源。其实这个案例是我们关注的重点错误了。
我们先看下堆栈情况
这里写图片描述
在本例子中,我们通过形参u改变了User实例的name值,让我们产生“此处传递的是对象的引用,会影响原来值的内容的错觉”
为什么说是错觉呢,因为原来参数的值压根没有改变啊。再回想一下值传递的定义,在本案例中,我们只是复制了user的地址,但是没有对地址做更改,u和user存储的值一直都是0x123456.如果Java存在引用传递,应该是这样的:将u的值改成0x123123,user的值也变化成0x123123。
这里写图片描述
所以,如果真要研究值传递和引用传递,第二个案例应该改成这样:

        //真正的案例2 
        User user2 = new User("Tom");
        System.out.println("user address in main is " + user);
        pass.pass2true(user);
        System.out.println("user address in main is " + user);
    private void pass2true(User user) {
        user = new User("Jerry");
        System.out.println("user address in pass2true is " + user);
    }

打印结果:

user address in main is test.PassByValue$User@15db9742
user address in pass2true is test.PassByValue$User@6d06d69c
user address in main is test.PassByValue$User@15db9742

可以看到,确实是值传递呢,对形参的操作没有影响实参的值。

这个例子可以这么理解,原来有一个遥控器控制电视,现在复制了遥控器,两个遥控器都可以控制电视,我们关注的重点应该是对遥控器二操作会不会影响遥控器一,而不应该认为:我用遥控器二调了台,我就认为遥控器二影响了遥控器一,其实遥控器一一直没变。

案例三分析
有了上面案例二的解释,这个案例就很好理解了。

pass.pass3(s);

我们拷贝的实参的地址给形参

str = "World";

其实就是

str = new String("World");

所以,形参的修改,没有影响实参的值,这个跟真正的案例二是一样的效果呢。

总结

我们需要重点关注案例二,注意关注点是参数的内容变化,而不是内容所指对象的变化。文章开头说到的案例其实就是最开始的案例二,现在也就明白了。
所以,Java说到底还是值传递啊。
记得N多年前,大学老师就是这么讲的:
“传递的参数如果是普通类型,那就是值传递,如果是对象,那就是引用传递。”
现在这么多年过去了,也能自己参悟其中的错误了,时间真是磨练人呢。我是不是也可以当老师了,哈哈(>_<)。

猜你喜欢

转载自blog.csdn.net/u011109881/article/details/80458946
今日推荐