Java中的对象引用传递

186 阅读3分钟

在工作中碰到一个问题,之前没在意过,代码简单复原下

public static void main(String[] args) {
    String[] aa = {};
    test(aa);
    System.out.println(Arrays.toString(aa));//打印发现aa还是空
}


public static void test(String[] aa) {
    aa = new String[]{"1", "2", "3"};
}

以上代码在test方法中对main方法传进来的数组aa进行重新赋值,但后续打印aa发现却是空数组

原因分析: java中的所有方法参数都是值传递的,将一个变量传递给一个方法时,实际上传递的是该变量的值而不是引用。 对于基本数据类型(如int、double等),传递的是它们的值副本;而对于对象引用,传递的是引用的值副本。 对于对象引用,传递的是“引用的值副本”,意思是指在调用函数或方法时,实际上传递的是对象引用的副本(即引用本身的一个拷贝),如果以上代码变成aa[0]="5",aa[1]="6"则main方法中的aa就会被改变,因为使用的是aa的引用副本去操作。但是像上面这样直接new一个新的数组进行赋值,则只是test方法中传递进来的aa数组引用改变成了新的数组(只是重新给test方法中的aa赋了一个引用),main方法中的aa并没有被改变。

当你传递一个数组aatest方法时,传递的是aa引用的副本。在test方法内部,如果你为aa重新赋值一个新的数组,比如aa = new String[]{"1", "2", "3"},这个操作只是将aa副本引用指向了一个新的数组对象,而并没有改变原始的aa引用。 因此,尽管在test方法内部进行了重新分配,但原始的aa引用仍然指向原来的空数组,因此在main方法中打印aa时,它仍然是空数组。 这种行为符合Java中的值传递机制,其中对象引用本身是通过值传递传递的(在将一个对象传递给方法时,实际上传递的是原始对象引用(原始对象在堆内存中的地址)的副本,而不是对象本身。),因此对传入方法的引用的重新分配不会影响原始的引用。

如以下两个代码

例一:传递对象引用副本,在方法中对副本引用重新分配了新的对象,只改变了副本引用的内容,原始对象不会收到影响

class Person {
    String name;

    public Person(String name) {
        this.name = name;
    }
}

void changeName(Person person) {
    person = new Person("Alice");
}

public static void main(String[] args) {
    Person person = new Person("Bob");
    changeName(person);
    System.out.println(person.name);  // 输出 "Bob"
}

例二:传递对象引用副本(原始对象在堆内存中的地址副本)给方法,未在方法中对引用副本重新分配对象,直接使用该引用副本去操作属性,原始对象也会被修改。因为被传递的引用和原始引用指向的是同一个对象,因此在方法内对该对象的修改会影响到原始引用。

class Person { 
    String name; 
    public Person(String name) { 
    this.name = name; 
    } 
 }

void changeName(Person person) {
    person.name = "Alice";
}

public static void main(String[] args) {
    Person person = new Person("Bob");
    changeName(person);
    System.out.println(person.name);  // 输出 "Alice"
}