Java“按值传递”和“按引用传递”释疑

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

在Java的说明书中,说明了Java是“按值传递(pass-by-value)”的,并不存在什么“按引用传递(pass-by-reference)”

但是我们在一些博客中,看到有说Java中既有“按值传递”,也有“按引用传递”,而且在我们的实际使用中,有些时候确实给我们感觉像是有时候在“按值传递”,有时候在“按引用传递”,这是为什么呢?

其实我们的疑惑都是因为概念混淆不清的原因,接下来我们就按着下面这些问题的顺序,来一步步搞清楚到底是怎么回事

  • 什么是我们感觉的按值传递,什么是我们感觉的按引用传递?

  • 为什么我们会感觉像是有时候在按值传递,有时候在按引用传递?

  • Java真正的传递是怎么回事?

 

在解决问题之前,我们先明确几个概念

指针(pointer)

当我们在定义一个变量或常量时,例 Integer a; 此处的a就是一个指针,它并不是一个实际的对象

对象(object)

当我们在实例化一个类时,在内存中会创建一个类的实例,即为对象,例 Integer a = new Integer(1); 在代码中我们并不能说哪一部分就是对象,new只是实例化的操作,我们会将实例化出的对象的地址,即引用,赋值给 a(即指针)

引用(inference)

当我们实例化出一个对象后,该对象在内存中的地址即为引用

 

什么是我们感觉的按值传递

在传递参数时,我们把 参数的数值 拷贝一份给被调用的方法,传递后就互不相关了(这仅仅是我们感觉的,是错误的,后续会说明)

什么是我们感觉的按引用传递

在传递参数时,我们把 参数的引用 传递给被调用的方法,后续的操作还是在操作这个参数(这仅仅是我们感觉的,是错误的,后续会说明)

什么我们会感觉像是有时候在按值传递,有时候在按引用传递

例1:

//例1
public static void main(String[] args){
    int a = 1;
    int[]arr = {1,2,3};
    call(a,arr);
    System.out.println(a);    //实际结果 1
    System.out.println(arr);  //实际结果 {0,2,3}
}

public static void call(int a,int[] arr){
    a = 0;
    arr[0] = 0;
}

在这个例子中,我们感觉 变量a 是按值传递了,数组arr 是按引用传递了

例2:

//例2
class Dog{
    public Dog(String name){
        this.name = name;
    }
    private String name;
    //省略getter/setter
}

class Test{
    public static void main(String[] args){
        Dog myDog = new Dog("我的狗");
        call(myDog);
        System.out.println(myDog.getName());    //实际结果 “别人的狗”

        Dog myDog2 = new Dog("我的狗");
        call2(myDog2);
        System.out.println(myDog2.getName());    //实际结果 “我的狗”
    }
    public static void call(Dog dog){
        dog.setName("别人的狗");
    }
    public static void call2(Dog dog){
        dog = new Dog("别人的第二条狗");
        dog.setName("别人的第三条狗");
    }
}

以我们的感觉来看,call()方法是引用传递,call2()方法是按值传递

当真是这样吗?

 

Java真正的传递是怎么回事

首先,我们必须要正确地定义,什么是 按值传递

此处传递的值,是指 引用的值,Java说明书中所表达的意思是,按“引用的值”传递

 

那么我们用这个定义去解释例2

    在call()方法中,myDog的引用被传递,call()方法操作了myDog引用指向的对象,setName()操作的是 原对象

    在call2()方法中,myDog2的引用被传递,call2()方法中重新new了一个对象出来,setName()操作的是 新生成的对象

 

这样,例二中产生的结果就可以被正确的解释和理解了,那么我们再去用这个定义去解释例1

    a的引用被call()方法操作,将a的值进行改变

    arr的引用被call()方法操作,将该arr[0]的值进行改变

 

我们会发现,a的值并没有改变,这个解释不通,那么我们就要来考虑Java内存模型的问题了

我们知道,在Java中,有两大种数据类型:基本数据类型和引用数据类型

基本数据类型:8种基础数据类型和字符串(在声明之后java就会立刻分配给他内存空间)

引用数据类型:即对象(在实例化之后才会分配内存空间)

 

在例1中,a是基本数据类型,当call()方法执行了 a=1 这个语句后,Java会立即给1分配内存空间,所以此时call()方法中的a实际指向的是 1 的内存地址,而已经不是原来的引用了

这样就能正确解释例1中的情况了

 

所以,我们最后可以知道,Java中所有的传递都是 按值传递(引用值),而所谓的 按引用传递,只是因为我们没有明白 值 的真正意思。

最后,本文参考自 stackOverflow的一个问题,附上该问题地址https://stackoverflow.com/questions/40480/is-java-pass-by-reference-or-pass-by-value

 

 

猜你喜欢

转载自blog.csdn.net/mxxrgxg/article/details/82499855